Search code examples
jerseyjersey-2.0bean-validationwebsphere-libertyjsr380

Does Jersey Extension for Bean Validation supports Bean Validation 2.0 (JSR 380)


I am working with WebSphere Liberty 19.0.0.2 with webProfile-8.0 feature which supports jaxrs-2.1 and beanValidation-2.0. For better support for MutiPart streaming I am using Jersey as JAX-RS implementation instead of liberty's default Apache CXF.

Some more contextual information about versioning of relevant components

Bean Validation 1.1 (JSR 349), Bean Validation 2.0 (JSR 380)

I want to use bean validation 2.0 feature to validate request/response/params in my REST Resource class and for that I need use Jersey's extension for bean validation. https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/bean-validation.html

As you can see on this link (if you scroll down) the latest version of the extension (2.30.1) also refer to JSR-349 which is bean validation 1.1.

MENIFEST of the jar from following gradle dependency also mention JSR-349

compile group: 'org.glassfish.jersey.ext', name: 'jersey-bean-validation', version: '2.30'

enter image description here

Its surprising that there is no Jersey extension for Bean Validation 2.0 (JSR 380).

When I use above dependency in liberty, bean validation works but I get following error on server startup because of bean validation annotation usage in my resource class.

[3/5/20 18:11:28:597 EST] 00000020 id=00000000 org.glassfish.jersey.model.Parameter                         2 Unable to get the com.sun.proxy.$Proxy70 annotation value property
java.lang.NoSuchMethodException: javax.validation.constraints.NotNull.value()
    at java.lang.Class.getMethod(Class.java:1786)
    at org.glassfish.jersey.model.Parameter.getValue(Parameter.java:453)
    at org.glassfish.jersey.model.Parameter.create(Parameter.java:270)
    at org.glassfish.jersey.model.Parameter.createList(Parameter.java:400)
    at org.glassfish.jersey.model.Parameter.createList(Parameter.java:383)
    at org.glassfish.jersey.server.model.Parameter.create(Parameter.java:137)
    at org.glassfish.jersey.server.model.Invocable.<init>(Invocable.java:215)
    at org.glassfish.jersey.server.model.Invocable.create(Invocable.java:161)
    at org.glassfish.jersey.server.model.ResourceMethod$Builder.createInvocable(ResourceMethod.java:541)
    at org.glassfish.jersey.server.model.ResourceMethod$Builder.build(ResourceMethod.java:522)
    at org.glassfish.jersey.server.model.Resource$Builder.processMethodBuilders(Resource.java:647)
    at org.glassfish.jersey.server.model.Resource$Builder.buildResourceData(Resource.java:583)
    at org.glassfish.jersey.server.model.Resource$Builder.build(Resource.java:639)
    at org.glassfish.jersey.server.model.Resource.from(Resource.java:782)
    at org.glassfish.jersey.server.ResourceBagConfigurator.init(ResourceBagConfigurator.java:55)
    at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:331)
    at org.glassfish.jersey.server.ApplicationHandler.lambda$initialize$1(ApplicationHandler.java:293)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
    at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:232)
    at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:292)
    at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:259)
    at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:311)
    at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:154)
    at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:347)
    at javax.servlet.GenericServlet.init(GenericServlet.java:244)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.init(ServletWrapper.java:291)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.loadOnStartupCheck(ServletWrapper.java:1373)
    at com.ibm.ws.webcontainer.webapp.WebApp.doLoadOnStartupActions(WebApp.java:1157)
    at com.ibm.ws.webcontainer.webapp.WebApp.commonInitializationFinally(WebApp.java:1125)
    at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1023)
    at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6619)
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:467)
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:462)
    at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1152)
    at com.ibm.ws.webcontainer.osgi.WebContainer.access$000(WebContainer.java:111)
    at com.ibm.ws.webcontainer.osgi.WebContainer$3.run(WebContainer.java:957)
    at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:239)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

[3/5/20 18:11:28:598 EST] 00000020 id=00000000 org.glassfish.jersey.model.Parameter                         2 Unable to get the com.sun.proxy.$Proxy29 annotation value property
java.lang.NoSuchMethodException: javax.validation.Valid.value()
    at java.lang.Class.getMethod(Class.java:1786)
    at org.glassfish.jersey.model.Parameter.getValue(Parameter.java:453)
    at org.glassfish.jersey.model.Parameter.create(Parameter.java:270)
    at org.glassfish.jersey.model.Parameter.createList(Parameter.java:400)
    at org.glassfish.jersey.model.Parameter.createList(Parameter.java:383)
    at org.glassfish.jersey.server.model.Parameter.create(Parameter.java:137)
    at org.glassfish.jersey.server.model.Invocable.<init>(Invocable.java:215)
    at org.glassfish.jersey.server.model.Invocable.create(Invocable.java:161)
    at org.glassfish.jersey.server.model.ResourceMethod$Builder.createInvocable(ResourceMethod.java:541)
    at org.glassfish.jersey.server.model.ResourceMethod$Builder.build(ResourceMethod.java:522)
    at org.glassfish.jersey.server.model.Resource$Builder.processMethodBuilders(Resource.java:647)

These error are concerning and not feeling confident to use it. Any advice or help is much appreciated.

Update 03/06/2020 Following is feature manager section from server.xml

<featureManager>
    <!--NOTE: Following are standard features and should not be removed-->
    <feature>servlet-4.0</feature>
    <feature>jndi-1.0</feature>
    <feature>requestTiming-1.0</feature>
    <feature>monitor-1.0</feature>
    <feature>localConnector-1.0</feature>
    <feature>restConnector-2.0</feature>
    <feature>ssl-1.0</feature>

    <!-- Do not add enabled webProfile-8.0 because we want to disable default 
    REST implementation (Apache-CXF) provided by Liberty. We want to use Jersey 
    as our REST implementation because it better support multi-part streaming, -->
    <!-- <feature>webProfile-8.0</feature> -->
    <feature>jsp-2.3</feature>
    <feature>cdi-2.0</feature>
    <feature>managedBeans-1.0</feature>
    <feature>jdbc-4.2</feature>
    <feature>beanValidation-2.0</feature>
    <!-- We need javaMail feature for logback email appender to work -->
    <feature>javaMail-1.6</feature>
</featureManager>

Here is my rest application configuration extending jersey's ResourceConfig class

import javax.ws.rs.ApplicationPath;

import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;

@ApplicationPath("/rest")
public class RestApplicationConfig extends ResourceConfig {

    public RestApplicationConfig() {
        super();
        configureResourcesAndFeatures();
    }

    private void configureResourcesAndFeatures() {
        packages(RestApplicationConfig.class.getPackage().getName());
        register(MultiPartFeature.class);

        property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    }
}

I mentioned use of jersey-bean-validation version 2.30 but I learned that Jersey 2.27 is latest version which is JAVA EE implementation of JAX-RS 2.1 API. Starting 2.28 its Jakarta EE implementation of JAX-RS 2.1. So I would really want to use version 2.27 and make bean validation 2.0 working with it because liberty version I am using has not up to jakarta ee yet.


Solution

  • Cause of all the issues was incorrect META-INF information packaged within jersey-bean-validation jar and also incorrect gradle dependency.

    If go to https://mvnrepository.com/artifact/org.glassfish.jersey.ext/jersey-bean-validation/2.27 url and scroll down if you see it includes validation-api 1.1.0.Final and hibernate-validator 5.1.3.Final which for Bean Validation 1.1 (JSR 349). enter image description here

    You have to exclude above incorrect transitive dependencies and include correct version for Bean Validation 2.0 (JSR 380) which is Hibernate Validator 6.0 (6.0.18.Final)

    If your container already provides implementation of bean validation, you don't need to include hibernate validator jar. In my case enabling beanValidation beanValidation-2.0 in liberty it provides bean validation implementation.

    // Jersey 2.27 is latest version which is JAVA EE implementation of JAX-RS 2.1 API. Starting 2.28 its Jakarta EE implementation of JAX-RS 2.1
    compile group: 'org.glassfish.jersey.containers', name: 'jersey-container-servlet', version: '2.27'
    compile group: 'org.glassfish.jersey.media', name: 'jersey-media-json-jackson', version: '2.27'
    compile group: 'org.glassfish.jersey.media', name: 'jersey-media-multipart', version: '2.27'
    compile (group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.27') {
        exclude group: 'javax.inject', module: 'javax.inject'
    }
    compile (group: 'org.glassfish.jersey.ext', name: 'jersey-bean-validation', version: '2.27') {
        exclude group: 'javax.el', module: 'javax.el-api'
        exclude group: 'org.hibernate'
    }
    
    configurations.compile {
        exclude group: 'javax.validation', module: 'validation-api'
        exclude group: 'javax.annotation', module: 'javax.annotation-api'
    }
    

    excluded 2 jars at compile configuration level because they were coming as transitive dependencies from multiple places.

    This fixed all issues and bean validation works like charm.