You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2020/10/21 12:51:28 UTC
[cxf] branch master updated: Enable to pass a bval Validator to BeanValidationProvider (it is more… (#695)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new 57f8847 Enable to pass a bval Validator to BeanValidationProvider (it is more… (#695)
57f8847 is described below
commit 57f88470951d981eccbe1f427cbe0398438f706d
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Wed Oct 21 14:50:44 2020 +0200
Enable to pass a bval Validator to BeanValidationProvider (it is more… (#695)
* Enable to pass a bval Validator to BeanValidationProvider (it is more than enough and faster than creating validators again and again) + enable to cache the fact no validation is needed
* shouldValidateBean must call isBeanConstrained and not hasConstraints, thanks @reta
---
.../validation/AbstractValidationInterceptor.java | 19 +++-
.../cxf/validation/BeanValidationProvider.java | 116 ++++++++++++++++-----
2 files changed, 108 insertions(+), 27 deletions(-)
diff --git a/core/src/main/java/org/apache/cxf/validation/AbstractValidationInterceptor.java b/core/src/main/java/org/apache/cxf/validation/AbstractValidationInterceptor.java
index 2f39152..f0e98d4 100644
--- a/core/src/main/java/org/apache/cxf/validation/AbstractValidationInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/validation/AbstractValidationInterceptor.java
@@ -36,11 +36,13 @@ import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.service.invoker.FactoryInvoker;
import org.apache.cxf.service.invoker.Invoker;
-public abstract class AbstractValidationInterceptor extends AbstractPhaseInterceptor< Message > {
+public abstract class AbstractValidationInterceptor extends AbstractPhaseInterceptor< Message >
+ implements AutoCloseable {
protected static final Logger LOG = LogUtils.getL7dLogger(AbstractValidationInterceptor.class);
protected static final ResourceBundle BUNDLE = BundleUtils.getBundle(AbstractValidationInterceptor.class);
private Object serviceObject;
+ private boolean customProvider;
private volatile BeanValidationProvider provider;
public AbstractValidationInterceptor(String phase) {
@@ -56,6 +58,13 @@ public abstract class AbstractValidationInterceptor extends AbstractPhaseInterce
}
@Override
+ public void close() {
+ if (customProvider) {
+ provider.close();
+ }
+ }
+
+ @Override
public void handleMessage(Message message) {
final Object theServiceObject = getServiceObject(message);
if (theServiceObject == null) {
@@ -125,7 +134,13 @@ public abstract class AbstractValidationInterceptor extends AbstractPhaseInterce
if (prop != null) {
provider = (BeanValidationProvider)prop;
} else {
- provider = new BeanValidationProvider();
+ // don't create 2 validator factories and one not released!
+ synchronized (this) {
+ if (provider == null) {
+ provider = new BeanValidationProvider();
+ customProvider = true;
+ }
+ }
}
}
return provider;
diff --git a/core/src/main/java/org/apache/cxf/validation/BeanValidationProvider.java b/core/src/main/java/org/apache/cxf/validation/BeanValidationProvider.java
index f105b1d..425832b 100644
--- a/core/src/main/java/org/apache/cxf/validation/BeanValidationProvider.java
+++ b/core/src/main/java/org/apache/cxf/validation/BeanValidationProvider.java
@@ -21,6 +21,9 @@ package org.apache.cxf.validation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Supplier;
import java.util.logging.Logger;
import javax.validation.Configuration;
@@ -30,20 +33,28 @@ import javax.validation.ParameterNameProvider;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.ValidationProviderResolver;
+import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import javax.validation.spi.ValidationProvider;
import org.apache.cxf.common.logging.LogUtils;
-public class BeanValidationProvider {
+import static java.util.Collections.emptySet;
+
+public class BeanValidationProvider implements AutoCloseable {
private static final Logger LOG = LogUtils.getL7dLogger(BeanValidationProvider.class);
- private final ValidatorFactory factory;
+ private final Runnable close;
+ private final Supplier<Validator> factory;
+ private final RuntimeCache runtimeCache; // /!must only be created when a single factory is used
public BeanValidationProvider() {
try {
- factory = Validation.buildDefaultValidatorFactory();
+ final ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
+ this.factory = vf::getValidator;
+ this.close = vf::close;
+ this.runtimeCache = new RuntimeCache();
} catch (final ValidationException ex) {
LOG.severe("Bean Validation provider can not be found, no validation will be performed");
throw ex;
@@ -58,18 +69,34 @@ public class BeanValidationProvider {
try {
Configuration<?> factoryCfg = Validation.byDefaultProvider().configure();
initFactoryConfig(factoryCfg, cfg);
- factory = factoryCfg.buildValidatorFactory();
+ final ValidatorFactory vf = factoryCfg.buildValidatorFactory();
+ this.factory = vf::getValidator;
+ this.close = vf::close;
+ this.runtimeCache = new RuntimeCache();
} catch (final ValidationException ex) {
LOG.severe("Bean Validation provider can not be found, no validation will be performed");
throw ex;
}
}
+ public BeanValidationProvider(Validator validator) {
+ if (validator == null) {
+ throw new NullPointerException("Validator is null");
+ }
+ this.factory = () -> validator;
+ this.close = () -> {
+ };
+ this.runtimeCache = new RuntimeCache();
+ }
+
public BeanValidationProvider(ValidatorFactory factory) {
if (factory == null) {
throw new NullPointerException("Factory is null");
}
- this.factory = factory;
+ this.factory = factory::getValidator;
+ this.close = () -> {
+ };
+ this.runtimeCache = new RuntimeCache();
}
public BeanValidationProvider(ValidationProviderResolver resolver) {
@@ -91,7 +118,11 @@ public class BeanValidationProvider {
? Validation.byProvider(providerType).providerResolver(resolver).configure()
: Validation.byDefaultProvider().providerResolver(resolver).configure();
initFactoryConfig(factoryCfg, cfg);
- factory = factoryCfg.buildValidatorFactory();
+ final ValidatorFactory vf = factoryCfg.buildValidatorFactory();
+ this.factory = vf::getValidator;
+ this.close = () -> {
+ };
+ this.runtimeCache = new RuntimeCache();
} catch (final ValidationException ex) {
LOG.severe("Bean Validation provider can not be found, no validation will be performed");
throw ex;
@@ -111,46 +142,81 @@ public class BeanValidationProvider {
}
public< T > void validateParameters(final T instance, final Method method, final Object[] arguments) {
-
- final ExecutableValidator methodValidator = getExecutableValidator();
- final Set< ConstraintViolation< T > > violations = methodValidator.validateParameters(instance,
- method, arguments);
-
- if (!violations.isEmpty()) {
- throw new ConstraintViolationException(violations);
+ final Validator validator = factory.get();
+ final ExecutableValidator methodValidator = validator.forExecutables();
+ if (runtimeCache == null || runtimeCache.shouldValidateParameters(validator, method)) {
+ final Set<ConstraintViolation<T>> violations = methodValidator.validateParameters(instance,
+ method, arguments);
+ if (!violations.isEmpty()) {
+ throw new ConstraintViolationException(violations);
+ }
}
}
public< T > void validateReturnValue(final T instance, final Method method, final Object returnValue) {
- final ExecutableValidator methodValidator = getExecutableValidator();
- final Set<ConstraintViolation< T > > violations = methodValidator.validateReturnValue(instance,
- method, returnValue);
-
- if (!violations.isEmpty()) {
- throw new ResponseConstraintViolationException(violations);
+ final Validator validator = factory.get();
+ final ExecutableValidator methodValidator = validator.forExecutables();
+ if (runtimeCache == null || runtimeCache.shouldValidateReturnedValue(validator, method)) {
+ final Set<ConstraintViolation<T>> violations = methodValidator.validateReturnValue(instance,
+ method, returnValue);
+ if (!violations.isEmpty()) {
+ throw new ResponseConstraintViolationException(violations);
+ }
}
}
public< T > void validateReturnValue(final T bean) {
- final Set<ConstraintViolation< T > > violations = doValidateBean(bean);
+ Validator validator = factory.get();
+ if (runtimeCache != null && bean != null
+ && !runtimeCache.shouldValidateBean(validator, bean.getClass())) {
+ return;
+ }
+ final Set<ConstraintViolation< T > > violations = doValidateBean(validator, bean);
if (!violations.isEmpty()) {
throw new ResponseConstraintViolationException(violations);
}
}
public< T > void validateBean(final T bean) {
- final Set<ConstraintViolation< T > > violations = doValidateBean(bean);
+ final Set<ConstraintViolation< T > > violations = doValidateBean(factory.get(), bean);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
- private< T > Set<ConstraintViolation< T > > doValidateBean(final T bean) {
- return factory.getValidator().validate(bean);
+ private< T > Set<ConstraintViolation< T > > doValidateBean(final Validator validator, final T bean) {
+ if (validator.getConstraintsForClass(bean.getClass()).isBeanConstrained()) {
+ return validator.validate(bean);
+ }
+ return emptySet();
+ }
+
+ @Override
+ public void close() {
+ close.run();
}
- private ExecutableValidator getExecutableValidator() {
+ // only created when there is a single validator/factory so it is safe to cache
+ // note: the validator is passed as param to avoid to create useless ones
+ private static class RuntimeCache {
+ private final ConcurrentMap<Class<?>, Boolean> types = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Method, Boolean> params = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Method, Boolean> returnedValues = new ConcurrentHashMap<>();
+
+ public boolean shouldValidateParameters(final Validator validator, final Method method) {
+ return params.computeIfAbsent(method, m -> validator.getConstraintsForClass(m.getDeclaringClass())
+ .getConstraintsForMethod(m.getName(), m.getParameterTypes())
+ .hasConstrainedParameters());
+ }
- return factory.getValidator().forExecutables();
+ public boolean shouldValidateReturnedValue(final Validator validator, final Method method) {
+ return returnedValues.computeIfAbsent(method, m -> validator.getConstraintsForClass(m.getDeclaringClass())
+ .getConstraintsForMethod(m.getName(), method.getParameterTypes())
+ .hasConstrainedReturnValue());
+ }
+
+ public boolean shouldValidateBean(final Validator validator, final Class<?> clazz) {
+ return types.computeIfAbsent(clazz, it -> validator.getConstraintsForClass(it).isBeanConstrained());
+ }
}
}