Permalink
Browse files

JERSEY-1583: Integration of Bean Validation into Jersey 2.x

 - new module (ext/bean-validation)
 - added property into ServerProperties to enable attaching validation errors into Response
 - server changes are mainly in model/internal (AbstractJavaResourceMethodDispatcher)
 - example (examples/bean-validation-webapp)
 - e2e/osgi tests
 - copyrights

Change-Id: I27f9b21c136accdec327e87b11974f6fbddc9243
  • Loading branch information...
1 parent f762a26 commit 7b8301f8c2eb81fe8e304ac5108a547184e67ee0 @mgajdos mgajdos committed Dec 14, 2012
Showing with 5,957 additions and 14 deletions.
  1. +6 −0 bundles/jax-rs-ri-bundle/pom.xml
  2. +2 −2 core-common/src/main/java/org/glassfish/jersey/internal/ExceptionMapperFactory.java
  3. +5 −0 core-server/pom.xml
  4. +16 −2 core-server/src/main/java/org/glassfish/jersey/server/ServerProperties.java
  5. +57 −0 core-server/src/main/java/org/glassfish/jersey/server/internal/inject/ConfiguredValidator.java
  6. +77 −1 ...rc/main/java/org/glassfish/jersey/server/model/internal/AbstractJavaResourceMethodDispatcher.java
  7. +11 −6 ...rc/main/java/org/glassfish/jersey/server/model/internal/JavaResourceMethodDispatcherProvider.java
  8. +6 −1 core-server/src/main/java/org/glassfish/jersey/server/model/internal/VoidVoidDispatcherProvider.java
  9. +1 −0 etc/config/copyright-exclude
  10. +107 −0 examples/bean-validation-webapp/README.html
  11. +110 −0 examples/bean-validation-webapp/pom.xml
  12. +138 −0 ...ation-webapp/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/MyApplication.java
  13. +80 −0 ...c/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/constraint/AtLeastOneContact.java
  14. +99 −0 ...on-webapp/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/constraint/HasId.java
  15. +121 −0 ...main/java/org/glassfish/jersey/examples/beanvalidation/webapp/constraint/NotEmptySearchField.java
  16. +69 −0 ...bapp/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/constraint/SearchType.java
  17. +133 −0 ...-webapp/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/domain/ContactCard.java
  18. +125 −0 ...c/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/resource/ContactCardResource.java
  19. +88 −0 ...pp/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/resource/SearchResource.java
  20. +165 −0 ...app/src/main/java/org/glassfish/jersey/examples/beanvalidation/webapp/service/StorageService.java
  21. +52 −0 examples/bean-validation-webapp/src/main/resources/ValidationMessages.properties
  22. +62 −0 examples/bean-validation-webapp/src/main/webapp/WEB-INF/web.xml
  23. +54 −0 examples/bean-validation-webapp/src/main/webapp/app.js
  24. +127 −0 examples/bean-validation-webapp/src/main/webapp/contact.html
  25. +155 −0 examples/bean-validation-webapp/src/main/webapp/controllers.js
  26. +9 −0 examples/bean-validation-webapp/src/main/webapp/css/bootstrap-responsive.min.css
  27. +9 −0 examples/bean-validation-webapp/src/main/webapp/css/bootstrap.min.css
  28. +55 −0 examples/bean-validation-webapp/src/main/webapp/directives.js
  29. BIN examples/bean-validation-webapp/src/main/webapp/img/glyphicons-halflings-white.png
  30. BIN examples/bean-validation-webapp/src/main/webapp/img/glyphicons-halflings.png
  31. +77 −0 examples/bean-validation-webapp/src/main/webapp/index.html
  32. +41 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-bootstrap-prettify.min.js
  33. +9 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-bootstrap.min.js
  34. +7 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-cookies.min.js
  35. +7 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-loader.min.js
  36. +10 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-resource.min.js
  37. +13 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular-sanitize.min.js
  38. +159 −0 examples/bean-validation-webapp/src/main/webapp/lib/angular.min.js
  39. +1 −0 examples/bean-validation-webapp/src/main/webapp/lib/bootstrap.min.js
  40. +2 −0 examples/bean-validation-webapp/src/main/webapp/lib/jquery.min.js
  41. +55 −0 examples/bean-validation-webapp/src/main/webapp/services.js
  42. +282 −0 ...ion-webapp/src/test/java/org/glassfish/jersey/examples/beanvalidation/webapp/ContactCardTest.java
  43. +4 −0 examples/extended-wadl-webapp/pom.xml
  44. +3 −0 ...l-webapp/src/test/java/org/glassfish/jersey/examples/extendedwadl/ExtendedWadlWebappOsgiTest.java
  45. +0 −1 examples/helloworld-webapp/pom.xml
  46. +0 −1 examples/multipart-webapp/pom.xml
  47. +7 −0 examples/osgi-helloworld-webapp/functional-test/pom.xml
  48. +3 −0 ...nctional-test/src/test/java/org/glassfish/jersey/examples/helloworld/test/AbstractWebAppTest.java
  49. +7 −0 examples/osgi-http-service/functional-test/pom.xml
  50. +3 −0 ...est/src/test/java/org/glassfish/jersey/examples/osgihttpservice/test/AbstractHttpServiceTest.java
  51. +1 −0 examples/pom.xml
  52. +108 −0 ext/bean-validation/pom.xml
  53. +91 −0 ...bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationConfiguration.java
  54. +102 −0 ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationError.java
  55. +64 −0 ext/bean-validation/src/main/java/org/glassfish/jersey/server/validation/ValidationFeature.java
  56. +97 −0 ...dation/src/main/java/org/glassfish/jersey/server/validation/internal/ConfiguredValidatorImpl.java
  57. +167 −0 ...main/java/org/glassfish/jersey/server/validation/internal/ConstraintViolationExceptionMapper.java
  58. +68 −0 ...ain/java/org/glassfish/jersey/server/validation/internal/InjectingConstraintValidatorFactory.java
  59. +206 −0 ...an-validation/src/main/java/org/glassfish/jersey/server/validation/internal/ValidationBinder.java
  60. +158 −0 ...c/main/java/org/glassfish/jersey/server/validation/internal/ValidationErrorMessageBodyWriter.java
  61. +1 −0 ext/pom.xml
  62. +25 −0 pom.xml
  63. +16 −0 tests/e2e/pom.xml
  64. +62 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicBadSubResource.java
  65. +245 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicResource.java
  66. +105 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicSubResource.java
  67. +632 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/BasicValidationTest.java
  68. +136 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ContactBean.java
  69. +63 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomBean.java
  70. +66 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigResource.java
  71. +235 −0 ...2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomConfigValidationTest.java
  72. +83 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/CustomValidation.java
  73. +47 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/Extended.java
  74. +65 −0 ...s/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/MultipleParamConstraint.java
  75. +80 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/NonEmptyNames.java
  76. +80 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/OneContact.java
  77. +67 −0 tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/server/validation/ParamConstraint.java
  78. +41 −0 tests/e2e/src/test/resources/ValidationMessages.properties
  79. +21 −0 tests/osgi/functional/pom.xml
  80. +59 −0 tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationResource.java
  81. +134 −0 tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/basic/BeanValidationTest.java
  82. +3 −0 tests/osgi/functional/src/test/java/org/glassfish/jersey/osgi/test/util/Helper.java
@@ -90,6 +90,12 @@
</dependency>
<dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<scope>provided</scope>
@@ -42,7 +42,7 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -94,7 +94,7 @@ public ExceptionMapperType(ExceptionMapper mapper, Class<? extends Throwable> ex
}
}
- private Set<ExceptionMapperType> exceptionMapperTypes = new HashSet<ExceptionMapperType>();
+ private Set<ExceptionMapperType> exceptionMapperTypes = new LinkedHashSet<ExceptionMapperType>();
/**
* Create new exception mapper factory initialized with {@link ServiceLocator
View
@@ -150,6 +150,11 @@
</dependency>
<dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
@@ -243,12 +243,12 @@
public static final String PROPERTY_WADL_GENERATOR_CONFIG = "jersey.config.server.wadl.generatorConfig";
/**
- * If true then disable WADL generation.
+ * If {@code true} then disable WADL generation.
* <p>
* By default WADL generation is automatically enabled, if JAXB is
* present in the classpath.
* <p>
- * The default value is false.
+ * The default value is {@code false}.
* </p>
* <p>
* The name of the configuration property is <code>{@value}</code>.
@@ -257,6 +257,20 @@
@SuppressWarnings("HtmlTagCanBeJavadocTag")
public static final String FEATURE_DISABLE_WADL = "jersey.config.server.wadl.disableWadl";
+ /**
+ * If {@code true} then enable sending of validation error entity in {@code Response} (validation has to be enabled by registering
+ * {@code ValidationFeature} in the application).
+ * <p>
+ * The default value is {@code false} and only status code is sent in the {@code Response}.
+ * </p>
+ * <p>
+ * The name of the configuration property is <code>{@value}</code>.
+ * </p>
+ */
+ @SuppressWarnings("HtmlTagCanBeJavadocTag")
+ public static final String FEATURE_OUTPUT_VALIDATION_ERROR_ENTITY
+ = "jersey.config.server.validation.enableOutputValidationErrorEntity";
+
private ServerProperties() {
// prevents instantiation
}
@@ -0,0 +1,57 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * http://glassfish.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+package org.glassfish.jersey.server.internal.inject;
+
+import javax.validation.Validator;
+
+import org.glassfish.jersey.spi.Contract;
+
+/**
+ * Configured {@link Validator} for Jersey validation purposes.
+ * <p/>
+ * Note: This interface exists to distinguish between injection of default (un-configured) instances of
+ * {@link Validator}/{@link javax.validation.ValidatorFactory} and an implementation of this interface.
+ *
+ * @author Michal Gajdos (michal.gajdos at oracle.com)
+ */
+@Contract
+public interface ConfiguredValidator extends Validator {
+}
@@ -43,15 +43,24 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Set;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+
import org.glassfish.jersey.internal.ProcessingException;
+import org.glassfish.jersey.server.internal.inject.ConfiguredValidator;
import org.glassfish.jersey.server.internal.process.MappableException;
import org.glassfish.jersey.server.model.Invocable;
import org.glassfish.jersey.server.spi.internal.ResourceMethodDispatcher;
+import com.google.common.collect.Sets;
+
/**
* Abstract resource method dispatcher that provides skeleton implementation of
* dispatching requests to a particular {@link Method Java method} using supplied
@@ -61,6 +70,9 @@
*/
abstract class AbstractJavaResourceMethodDispatcher implements ResourceMethodDispatcher {
+ @Inject
+ private javax.inject.Provider<ConfiguredValidator> validatorProvider;
+
private final Method method;
private final InvocationHandler methodHandler;
@@ -105,7 +117,17 @@ public final Response dispatch(Object resource, Request request) throws Processi
*/
final Object invoke(Object resource, Object... args) throws ProcessingException {
try {
- return methodHandler.invoke(resource, method, args);
+ final ConfiguredValidator validator = validatorProvider.get();
+
+ // Validate resource class & method input parameters.
+ validateInput(validator, resource, args);
+
+ final Object invocationResult = methodHandler.invoke(resource, method, args);
+
+ // Validate response entity.
+ validateResult(validator, resource, invocationResult);
+
+ return invocationResult;
} catch (IllegalAccessException ex) {
throw new ProcessingException("Resource Java method invocation error.", ex);
} catch (InvocationTargetException ex) {
@@ -127,6 +149,60 @@ final Object invoke(Object resource, Object... args) throws ProcessingException
}
}
+ /**
+ * Validates resource class instance and input parameters of the {@code method}. {@link ConstraintViolationException} raised
+ * from this method should be mapped to HTTP 400 status.
+ *
+ * @param validator validator used to validate.
+ * @param resource resource class instance.
+ * @param args input method parameters.
+ * @throws ConstraintViolationException if {@link ConstraintViolation} occurs (should be mapped to HTTP 400 status).
+ */
+ private void validateInput(final Validator validator, final Object resource, final Object[] args)
+ throws ConstraintViolationException {
+
+ if (validator != null) {
+ final Set<ConstraintViolation<Object>> constraintViolations = Sets.newHashSet();
+
+ // Resource validation.
+ constraintViolations.addAll(validator.validate(resource));
+
+ // Resource method validation - input parameters.
+ constraintViolations.addAll(validator.forMethods().validateParameters(resource, method, args));
+
+ if (!constraintViolations.isEmpty()) {
+ throw new ConstraintViolationException(constraintViolations);
+ }
+ }
+ }
+
+ /**
+ * Validates response instance / response entity of the {@code method}. {@link ConstraintViolationException} raised
+ * from this method should be mapped to HTTP 500 status.
+ *
+ * @param validator validator used to validate.
+ * @param resource resource class instance.
+ * @param invocationResult response.
+ * @throws ConstraintViolationException if {@link ConstraintViolation} occurs (should be mapped to HTTP 500 status).
+ */
+ private void validateResult(final Validator validator, final Object resource, final Object invocationResult) {
+ // Resource method validation - return invocationResult.
+ if (validator != null) {
+ final Set<ConstraintViolation<Object>> constraintViolations = Sets.newHashSet();
+
+ constraintViolations.addAll(validator.forMethods().validateReturnValue(resource, method, invocationResult));
+
+ if (invocationResult instanceof Response) {
+ constraintViolations.addAll(
+ validator.forMethods().validateReturnValue(resource, method, ((Response) invocationResult).getEntity()));
+ }
+
+ if (!constraintViolations.isEmpty()) {
+ throw new ConstraintViolationException(constraintViolations);
+ }
+ }
+ }
+
@Override
public String toString() {
return method.toString();
@@ -71,24 +71,29 @@
@Override
public ResourceMethodDispatcher create(Invocable resourceMethod, InvocationHandler invocationHandler) {
-
final List<Factory<?>> valueProviders = resourceMethod.getValueProviders(serviceLocator);
-
final Class<?> returnType = resourceMethod.getHandlingMethod().getReturnType();
+
+ ResourceMethodDispatcher resourceMethodDispatcher;
if (Response.class.isAssignableFrom(returnType)) {
- return new ResponseOutInvoker(resourceMethod, invocationHandler, valueProviders);
+ resourceMethodDispatcher = new ResponseOutInvoker(resourceMethod, invocationHandler, valueProviders);
// TODO should we support JResponse?
// } else if (JResponse.class.isAssignableFrom(returnType)) {
// return new JResponseOutInvoker(resourceMethod, pp, invocationHandler);
} else if (returnType != void.class) {
if (returnType == Object.class || GenericEntity.class.isAssignableFrom(returnType)) {
- return new ObjectOutInvoker(resourceMethod, invocationHandler, valueProviders);
+ resourceMethodDispatcher = new ObjectOutInvoker(resourceMethod, invocationHandler, valueProviders);
} else {
- return new TypeOutInvoker(resourceMethod, invocationHandler, valueProviders);
+ resourceMethodDispatcher = new TypeOutInvoker(resourceMethod, invocationHandler, valueProviders);
}
} else {
- return new VoidOutInvoker(resourceMethod, invocationHandler, valueProviders);
+ resourceMethodDispatcher = new VoidOutInvoker(resourceMethod, invocationHandler, valueProviders);
}
+
+ // Inject validator.
+ serviceLocator.inject(resourceMethodDispatcher);
+
+ return resourceMethodDispatcher;
}
/**
@@ -41,6 +41,8 @@
import java.lang.reflect.InvocationHandler;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
@@ -61,6 +63,9 @@
@Singleton
final class VoidVoidDispatcherProvider implements ResourceMethodDispatcher.Provider {
+ @Context
+ private ResourceContext resourceContext;
+
private static class VoidToVoidDispatcher extends AbstractJavaResourceMethodDispatcher {
private VoidToVoidDispatcher(Invocable resourceMethod, InvocationHandler handler) {
@@ -80,6 +85,6 @@ public ResourceMethodDispatcher create(Invocable resourceMethod, InvocationHandl
return null;
}
- return new VoidToVoidDispatcher(resourceMethod, handler);
+ return resourceContext.initResource(new VoidToVoidDispatcher(resourceMethod, handler));
}
}
@@ -45,3 +45,4 @@ truststore_client
truststore_server
/examples/reload/resources
/core-server/etc
+bootstrap
Oops, something went wrong.

0 comments on commit 7b8301f

Please sign in to comment.