summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorDenis Anisimov <denis@vaadin.com>2016-09-18 09:53:39 +0300
committerDenis Anisimov <denis@vaadin.com>2016-09-23 11:08:56 +0000
commit0b5e2f34690a2a0f090a93ea1a63475a22ac3d13 (patch)
treea859f64029b513ee0c756e2b736d60fe2321dbab /server
parentcef5b51d3ff0792ab0a8a8423549dbff79e3027b (diff)
downloadvaadin-framework-0b5e2f34690a2a0f090a93ea1a63475a22ac3d13.tar.gz
vaadin-framework-0b5e2f34690a2a0f090a93ea1a63475a22ac3d13.zip
Extract validation availability check out of BeanValidator (#251).
BeanValidator uses and depends on javax.validation classes. So JSR303 presence check is extracted out of it to avoid ClassNotFoundException. Change-Id: I8df9a9da873cf694a326c9abb05315c8e94a0a96
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/data/BeanBinder.java4
-rw-r--r--server/src/main/java/com/vaadin/data/util/BeanUtil.java34
-rw-r--r--server/src/main/java/com/vaadin/data/validator/BeanValidator.java89
-rw-r--r--server/src/test/java/com/vaadin/data/Jsr303Test.java118
4 files changed, 192 insertions, 53 deletions
diff --git a/server/src/main/java/com/vaadin/data/BeanBinder.java b/server/src/main/java/com/vaadin/data/BeanBinder.java
index b27e3ee4c3..c73b89de81 100644
--- a/server/src/main/java/com/vaadin/data/BeanBinder.java
+++ b/server/src/main/java/com/vaadin/data/BeanBinder.java
@@ -176,7 +176,7 @@ public class BeanBinder<BEAN> extends Binder<BEAN> {
finalBinding = withConverter(createConverter());
- if (BeanValidator.checkBeanValidationAvailable()) {
+ if (BeanUtil.checkBeanValidationAvailable()) {
finalBinding = finalBinding.withValidator(new BeanValidator(
getBinder().beanType, propertyName, findLocale()));
}
@@ -262,7 +262,7 @@ public class BeanBinder<BEAN> extends Binder<BEAN> {
* the bean {@code Class} instance, not null
*/
public BeanBinder(Class<? extends BEAN> beanType) {
- BeanValidator.checkBeanValidationAvailable();
+ BeanUtil.checkBeanValidationAvailable();
this.beanType = beanType;
}
diff --git a/server/src/main/java/com/vaadin/data/util/BeanUtil.java b/server/src/main/java/com/vaadin/data/util/BeanUtil.java
index 579eb08b68..763e6ec4b1 100644
--- a/server/src/main/java/com/vaadin/data/util/BeanUtil.java
+++ b/server/src/main/java/com/vaadin/data/util/BeanUtil.java
@@ -23,6 +23,9 @@ import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import java.util.logging.Logger;
+
+import com.vaadin.data.validator.BeanValidator;
/**
* Utility class for Java Beans information access.
@@ -145,6 +148,20 @@ public final class BeanUtil implements Serializable {
}
}
+ /**
+ * Returns whether an implementation of JSR-303 version 1.0 or 1.1 is
+ * present on the classpath. If this method returns false, trying to create
+ * a {@code BeanValidator} instance will throw an
+ * {@code IllegalStateException}. If an implementation is not found, logs a
+ * level {@code FINE} message the first time it is run.
+ *
+ * @return {@code true} if bean validation is available, {@code false}
+ * otherwise.
+ */
+ public static boolean checkBeanValidationAvailable() {
+ return LazyValidationAvailability.BEAN_VALIDATION_AVAILABLE;
+ }
+
// Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+.
private static List<PropertyDescriptor> getPropertyDescriptors(
BeanInfo beanInfo) {
@@ -206,4 +223,21 @@ public final class BeanUtil implements Serializable {
return null;
}
}
+
+ private static class LazyValidationAvailability implements Serializable {
+ private static final boolean BEAN_VALIDATION_AVAILABLE = isAvailable();
+
+ private static boolean isAvailable() {
+ try {
+ Class.forName("javax.validation.Validation");
+ return true;
+ } catch (ClassNotFoundException e) {
+ Logger.getLogger(BeanValidator.class.getName())
+ .fine("A JSR-303 bean validation implementation not found on the classpath. "
+ + BeanValidator.class.getSimpleName()
+ + " cannot be used.");
+ return false;
+ }
+ }
+ }
}
diff --git a/server/src/main/java/com/vaadin/data/validator/BeanValidator.java b/server/src/main/java/com/vaadin/data/validator/BeanValidator.java
index 3b779b822d..9ac9c9a649 100644
--- a/server/src/main/java/com/vaadin/data/validator/BeanValidator.java
+++ b/server/src/main/java/com/vaadin/data/validator/BeanValidator.java
@@ -16,11 +16,11 @@
package com.vaadin.data.validator;
+import java.io.Serializable;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.BinaryOperator;
-import java.util.logging.Logger;
import javax.validation.ConstraintViolation;
import javax.validation.MessageInterpolator.Context;
@@ -30,6 +30,7 @@ import javax.validation.metadata.ConstraintDescriptor;
import com.vaadin.data.Result;
import com.vaadin.data.Validator;
+import com.vaadin.data.util.BeanUtil;
/**
* A {@code Validator} using the JSR-303 (javax.validation) annotation-based
@@ -43,46 +44,36 @@ import com.vaadin.data.Validator;
* project classpath when using bean validation. Specification versions 1.0 and
* 1.1 are supported.
*
- * @author Petri Hakala
* @author Vaadin Ltd.
*
* @since 8.0
*/
public class BeanValidator implements Validator<Object> {
- private static volatile Boolean beanValidationAvailable;
- private static ValidatorFactory factory;
+ private static final class ContextImpl implements Context, Serializable {
- private String propertyName;
- private Class<?> beanType;
- private Locale locale;
+ private final ConstraintViolation<?> violation;
- /**
- * Returns whether an implementation of JSR-303 version 1.0 or 1.1 is
- * present on the classpath. If this method returns false, trying to create
- * a {@code BeanValidator} instance will throw an
- * {@code IllegalStateException}. If an implementation is not found, logs a
- * level {@code FINE} message the first time it is run.
- *
- * @return {@code true} if bean validation is available, {@code false}
- * otherwise.
- */
- public static boolean checkBeanValidationAvailable() {
- if (beanValidationAvailable == null) {
- try {
- Class.forName(Validation.class.getName());
- beanValidationAvailable = true;
- } catch (ClassNotFoundException e) {
- Logger.getLogger(BeanValidator.class.getName())
- .fine("A JSR-303 bean validation implementation not found on the classpath. "
- + BeanValidator.class.getSimpleName()
- + " cannot be used.");
- beanValidationAvailable = false;
- }
+ private ContextImpl(ConstraintViolation<?> violation) {
+ this.violation = violation;
}
- return beanValidationAvailable;
+
+ @Override
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return violation.getConstraintDescriptor();
+ }
+
+ @Override
+ public Object getValidatedValue() {
+ return violation.getInvalidValue();
+ }
+
}
+ private String propertyName;
+ private Class<?> beanType;
+ private Locale locale;
+
/**
* Creates a new JSR-303 {@code BeanValidator} that validates values of the
* specified property. Localizes validation messages using the
@@ -93,7 +84,8 @@ public class BeanValidator implements Validator<Object> {
* @param propertyName
* the property to validate, not null
* @throws IllegalStateException
- * if {@link #checkBeanValidationAvailable()} returns false
+ * if {@link BeanUtil#checkBeanValidationAvailable()} returns
+ * false
*/
public BeanValidator(Class<?> beanType, String propertyName) {
this(beanType, propertyName, Locale.getDefault());
@@ -110,11 +102,12 @@ public class BeanValidator implements Validator<Object> {
* @param locale
* the locale to use, not null
* @throws IllegalStateException
- * if {@link #checkBeanValidationAvailable()} returns false
+ * if {@link BeanUtil#checkBeanValidationAvailable()} returns
+ * false
*/
public BeanValidator(Class<?> beanType, String propertyName,
Locale locale) {
- if (!checkBeanValidationAvailable()) {
+ if (!BeanUtil.checkBeanValidationAvailable()) {
throw new IllegalStateException("Cannot create a "
+ BeanValidator.class.getSimpleName()
+ ": a JSR-303 Bean Validation implementation not found on theclasspath");
@@ -170,11 +163,7 @@ public class BeanValidator implements Validator<Object> {
* @return the validator factory to use
*/
protected static ValidatorFactory getJavaxBeanValidatorFactory() {
- if (factory == null) {
- checkBeanValidationAvailable();
- factory = Validation.buildDefaultValidatorFactory();
- }
- return factory;
+ return LazyFactoryInitializer.FACTORY;
}
/**
@@ -203,22 +192,12 @@ public class BeanValidator implements Validator<Object> {
* Creates a simple message interpolation context based on the given
* constraint violation.
*
- * @param v
+ * @param violation
* the constraint violation
* @return the message interpolation context
*/
- protected Context createContext(ConstraintViolation<?> v) {
- return new Context() {
- @Override
- public ConstraintDescriptor<?> getConstraintDescriptor() {
- return v.getConstraintDescriptor();
- }
-
- @Override
- public Object getValidatedValue() {
- return v.getInvalidValue();
- }
- };
+ protected Context createContext(ConstraintViolation<?> violation) {
+ return new ContextImpl(violation);
}
/**
@@ -232,4 +211,12 @@ public class BeanValidator implements Validator<Object> {
Objects.requireNonNull(locale, "locale cannot be null");
this.locale = locale;
}
+
+ private static class LazyFactoryInitializer implements Serializable {
+ private static final ValidatorFactory FACTORY = getFactory();
+
+ private static ValidatorFactory getFactory() {
+ return Validation.buildDefaultValidatorFactory();
+ }
+ }
}
diff --git a/server/src/test/java/com/vaadin/data/Jsr303Test.java b/server/src/test/java/com/vaadin/data/Jsr303Test.java
new file mode 100644
index 0000000000..876fb534a3
--- /dev/null
+++ b/server/src/test/java/com/vaadin/data/Jsr303Test.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * Licensed 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 com.vaadin.data;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.validation.Validation;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.data.util.BeanUtil;
+import com.vaadin.tests.data.bean.BeanToValidate;
+import com.vaadin.ui.TextField;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class Jsr303Test {
+
+ private static class TestClassLoader extends URLClassLoader {
+
+ public TestClassLoader() {
+ super(new URL[0], Thread.currentThread().getContextClassLoader());
+ }
+
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ String vaadinPackagePrefix = getClass().getPackage().getName();
+ vaadinPackagePrefix = vaadinPackagePrefix.substring(0,
+ vaadinPackagePrefix.lastIndexOf('.'));
+ if (name.equals(UnitTest.class.getName())) {
+ super.loadClass(name);
+ } else if (name
+ .startsWith(Validation.class.getPackage().getName())) {
+ throw new ClassNotFoundException();
+ } else if (name.startsWith(vaadinPackagePrefix)) {
+ String path = name.replace('.', '/').concat(".class");
+ URL resource = Thread.currentThread().getContextClassLoader()
+ .getResource(path);
+ InputStream stream;
+ try {
+ stream = resource.openStream();
+ byte[] bytes = IOUtils.toByteArray(stream);
+ return defineClass(name, bytes, 0, bytes.length);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return super.loadClass(name);
+ }
+ }
+
+ public interface UnitTest {
+ void execute();
+ }
+
+ public static class Jsr303UnitTest implements UnitTest {
+
+ private TextField nameField = new TextField();
+
+ @Override
+ public void execute() {
+ Assert.assertFalse(BeanUtil.checkBeanValidationAvailable());
+
+ BeanBinder<BeanToValidate> binder = new BeanBinder<>(
+ BeanToValidate.class);
+ BeanToValidate item = new BeanToValidate();
+ String name = "Johannes";
+ item.setFirstname(name);
+ item.setAge(32);
+
+ binder.bind(nameField, "firstname");
+ binder.bind(item);
+
+ assertEquals(name, nameField.getValue());
+
+ // BeanToValidate : @Size(min = 3, max = 16) for the firstName
+ nameField.setValue("a");
+ assertEquals(nameField.getValue(), item.getFirstname());
+ }
+
+ }
+
+ @Test
+ public void beanBinderWithoutJsr303() throws ClassNotFoundException,
+ NoSuchMethodException, SecurityException, InstantiationException,
+ IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException, IOException, InterruptedException {
+ URLClassLoader loader = new TestClassLoader();
+ Class<?> clazz = loader.loadClass(Jsr303UnitTest.class.getName());
+ UnitTest test = (UnitTest) clazz.newInstance();
+ test.execute();
+ loader.close();
+ }
+
+}