Skip to content

Commit

Permalink
[SCB-2881] support setting hibernate validation configuration (#4341)
Browse files Browse the repository at this point in the history
  • Loading branch information
chengyouling authored May 23, 2024
1 parent 71591e8 commit 0d56846
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import java.util.concurrent.CompletableFuture;

import javax.annotation.Nonnull;
import javax.validation.Configuration;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import javax.validation.groups.Default;

import org.apache.servicecomb.config.ConfigUtil;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.core.filter.ProducerFilter;
Expand All @@ -40,21 +42,28 @@
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.netflix.config.DynamicPropertyFactory;

@Component
public class ParameterValidatorFilter implements ProducerFilter, InitializingBean {
public class ParameterValidatorFilter implements ProducerFilter, ApplicationContextAware, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class);

public static final String NAME = "validator";

private static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator";

public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator";

protected ExecutableValidator validator;

private ApplicationContext applicationContext;

@Nonnull
@Override
public String getName() {
Expand All @@ -67,12 +76,23 @@ public void afterPropertiesSet() {
.getValidator().forExecutables();
}

protected ValidatorFactory createValidatorFactory() {
return Validation.byProvider(HibernateValidator.class)
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

private ValidatorFactory createValidatorFactory() {
Configuration<?> validatorConfiguration = Validation.byProvider(HibernateValidator.class)
.configure()
.propertyNodeNameProvider(new JacksonPropertyNodeNameProvider())
.messageInterpolator(messageInterpolator())
.buildValidatorFactory();
.messageInterpolator(messageInterpolator());
Set<String> keys = ConfigUtil.propertiesWithPrefix(applicationContext.getEnvironment(), HIBERNATE_VALIDATE_PREFIX);
if (!keys.isEmpty()) {
for (String key : keys) {
validatorConfiguration.addProperty(key, applicationContext.getEnvironment().getProperty(key));
}
}
return validatorConfiguration.buildValidatorFactory();
}

protected AbstractMessageInterpolator messageInterpolator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;

import com.fasterxml.jackson.annotation.JsonProperty;

Expand Down Expand Up @@ -96,6 +100,12 @@ public void op(@NotNull(message = "not null") String query, @Valid Model model)

@BeforeClass
public static void beforeClass() throws Exception {
ApplicationContext applicationContext = Mockito.mock(ApplicationContext.class);
ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class);
MutablePropertySources sources = new MutablePropertySources();
Mockito.when(applicationContext.getEnvironment()).thenReturn(environment);
Mockito.when(environment.getPropertySources()).thenReturn(sources);
filter.setApplicationContext(applicationContext);
filter.afterPropertiesSet();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.servicecomb.swagger.invocation.validator;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.configuration.Configuration;

import com.netflix.config.DynamicPropertyFactory;

public final class ConfigurationPropertyUtils {
private ConfigurationPropertyUtils() {
}

/**
* 获取key包含prefix前缀的所有配置项
*/
public static Map<String, String> getPropertiesWithPrefix(String prefix) {
Object config = DynamicPropertyFactory.getBackingConfigurationSource();
if (!Configuration.class.isInstance(config)) {
return new HashMap<>();
}

return getPropertiesWithPrefix((Configuration) config, prefix);
}

// caller ensure configuration is valid
public static Map<String, String> getPropertiesWithPrefix(Configuration configuration, String prefix) {
Map<String, String> propertiesMap = new HashMap<>();

Iterator<String> keysIterator = configuration.getKeys(prefix);
while (keysIterator.hasNext()) {
String key = keysIterator.next();
propertiesMap.put(key.substring(prefix.length() + 1), String.valueOf(configuration.getProperty(key)));
}
return propertiesMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/
package org.apache.servicecomb.swagger.invocation.validator;

import java.util.Map;
import java.util.Set;

import javax.validation.Configuration;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
Expand Down Expand Up @@ -45,6 +47,8 @@ public class ParameterValidator implements ProducerInvokeExtension {

private static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator";

public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator";

private final DynamicBooleanProperty paramValidationEnabled = DynamicPropertyFactory.getInstance()
.getBooleanProperty(PARAM_VALIDATION_ENABLED, true);

Expand All @@ -63,13 +67,7 @@ public <T> void beforeMethodInvoke(SwaggerInvocation invocation, SwaggerProducer
throws ConstraintViolationException {
if (paramValidationEnabled.get()) {
if (null == executableValidator) {
ValidatorFactory factory =
Validation.byDefaultProvider()
.configure()
.parameterNameProvider(new DefaultParameterNameProvider())
.messageInterpolator(messageInterpolator())
.buildValidatorFactory();
executableValidator = factory.getValidator().forExecutables();
executableValidator = createValidatorFactory().getValidator().forExecutables();
}
Set<ConstraintViolation<Object>> violations =
executableValidator.validateParameters(producerOperation.getProducerInstance(),
Expand All @@ -83,6 +81,20 @@ public <T> void beforeMethodInvoke(SwaggerInvocation invocation, SwaggerProducer
}
}

private ValidatorFactory createValidatorFactory() {
Configuration<?> validatorConfiguration = Validation.byDefaultProvider()
.configure()
.parameterNameProvider(new DefaultParameterNameProvider())
.messageInterpolator(messageInterpolator());
Map<String, String> configs = ConfigurationPropertyUtils.getPropertiesWithPrefix(HIBERNATE_VALIDATE_PREFIX);
if (!configs.isEmpty()) {
for (Map.Entry<String, String> entry : configs.entrySet()) {
validatorConfiguration.addProperty(HIBERNATE_VALIDATE_PREFIX + "." + entry.getKey(), entry.getValue());
}
}
return validatorConfiguration.buildValidatorFactory();
}

private AbstractMessageInterpolator messageInterpolator() {
if (useResourceBundleMessageInterpolator()) {
return new ResourceBundleMessageInterpolator();
Expand Down

0 comments on commit 0d56846

Please sign in to comment.