finalBinding = withConverter(createConverter());
- if (BeanValidator.checkBeanValidationAvailable()) {
+ if (BeanUtil.checkBeanValidationAvailable()) {
finalBinding = finalBinding.withValidator(new BeanValidator(
getBinder().beanType, propertyName, findLocale()));
}
* the bean {@code Class} instance, not null
*/
public BeanBinder(Class<? extends BEAN> beanType) {
- BeanValidator.checkBeanValidationAvailable();
+ BeanUtil.checkBeanValidationAvailable();
this.beanType = beanType;
}
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.
}
}
+ /**
+ * 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) {
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;
+ }
+ }
+ }
}
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;
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
* 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
* @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());
* @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");
* @return the validator factory to use
*/
protected static ValidatorFactory getJavaxBeanValidatorFactory() {
- if (factory == null) {
- checkBeanValidationAvailable();
- factory = Validation.buildDefaultValidatorFactory();
- }
- return factory;
+ return LazyFactoryInitializer.FACTORY;
}
/**
* 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);
}
/**
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();
+ }
+ }
}
--- /dev/null
+/*
+ * 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();
+ }
+
+}