This repository has been archived by the owner. It is now read-only.
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...
mgajdos committed Dec 14, 2012
1 parent f762a26 commit 7b8301f8c2eb81fe8e304ac5108a547184e67ee0
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
@@ -89,6 +89,12 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
@@ -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
@@ -149,6 +149,11 @@
<artifactId>hk2-locator</artifactId>
</dependency>

<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -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.