Oromis
Oromis

Reputation: 316

Tomee 9 with jax-rs always returns error 404 not found

I am trying to create a simple Rest API with Java 11, jax-rs and Tomee Plume 9 for the server.
After many conflicts between javax.X and jakarta.X, I was finally able to compile the project. But when I try to run it on Intellij Idea, I get a 404 error on all my requests... I have no errors in the logs.

Here my web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         version="5.0"
         metadata-complete="true">

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>fr.theogiraudet.rest</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>

And an extract of my Rest resource, located in the package fr.theogiraudet.rest:

@Path("/pianos")
public class PianoResource {

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response getAllPianos(@Context UriInfo uriInfo) {
        final var optParams = filter(uriInfo);
        if (optParams.isEmpty())
            return Response.status(Response.Status.BAD_REQUEST).build();

        final var listOpt = dao.getAllPianos(optParams.get());
        if (listOpt.isEmpty())
            return Response.serverError().build();

        return Response.ok().entity(listOpt.get().toArray(new Piano[0])).build();
    }
}

My request: GET - http://localhost:8080/api/pianos
The application context is /.
At server startup, the war seems to be well located and deployed, as seen in logs:

10-Aug-2021 21:09:58.897 INFOS [http-nio-8080-exec-4] org.apache.openejb.assembler.classic.Assembler.createApplication Deployed Application(path=E:\Programmation\IntelliJ\Pause Piano - Backend\target\pause-piano-backend-1.0-SNAPSHOT)
10-Aug-2021 21:09:59.188 INFOS [http-nio-8080-exec-4] org.apache.jasper.servlet.TldScanner.scanJars Au moins un fichier JAR a été analysé pour trouver des TLDs mais il n'en contenait pas, le mode "debug" du journal peut être activé pour obtenir une liste complète de JAR scannés sans succès ; éviter d'analyser des JARs inutilement peut améliorer sensiblement le temps de démarrage et le temps de compilation des JSPs
[2021-08-10 09:10:00,169] Artifact Pause Piano - Backend:war: Artifact is deployed successfully
[2021-08-10 09:10:00,169] Artifact Pause Piano - Backend:war: Deploy took 4,904 milliseconds

The project can be found here if you need more information about the code (like pom.xml): https://github.com/Pause-Piano/PausePiano-Backend

Upvotes: 1

Views: 1373

Answers (1)

rzo1
rzo1

Reputation: 5751

tl;dr

You are using a version of swagger, which does not support the jakarta namespace. You have to upgrade to 2.1.7 or higher and add the related -jakarta suffix to the artifact descriptors. In addition, you are using an unsupported jackson, which does not support jakarta as well. You have to switch to jackson-jakarta-rs-providers (see details below).

Long explanation

To reproduce the deployment process I quickly added the following plugin configuration to your pom.xml to run TomEE from within Maven via tomee:run

        <plugin>
            <groupId>org.apache.tomee.maven</groupId>
            <artifactId>tomee-maven-plugin</artifactId>
            <version>8.0.7</version>
            <configuration>
                <tomeeVersion>9.0.0-M7</tomeeVersion>
                <tomeeClassifier>plume</tomeeClassifier>
                <debug>true</debug>
                <tomeeHttpPort>8282</tomeeHttpPort>
                <debugPort>5005</debugPort>
                <args>-Dfoo=bar</args>
                <config>${project.basedir}/src/test/tomee/conf</config>
                <skipCurrentProject>true</skipCurrentProject>
                <webapps>
                    <webapp>
                        fr.pause-piano:pause-piano-backend:1.0-SNAPSHOT?name=ROOT
                    </webapp>
                </webapps>
            </configuration>
        </plugin>

Then it tries to deploy your webapp, it will print some ClassNotFoundExceptions to the console output indicating some hassle between javax and jakarta namespaces.

You are using a version of swagger, which does not support the jakarta namespace. The official repository on GitHub states:

NOTE: Since version 2.1.7 Swagger Core supports also Jakarta namespace, with a parallel set of artifacts with -jakarta suffix, providing the same functionality as the "standard" javax namespace ones. Please check Wiki for more details

That means, you have to upgrade the related artifacts in and suffix in your code to:

    <dependency>
        <groupId>io.swagger.core.v3</groupId>
        <artifactId>swagger-jaxrs2-jakarta</artifactId>
        <version>2.1.10</version>
    </dependency>

    <dependency>
        <groupId>io.swagger.core.v3</groupId>
        <artifactId>swagger-jaxrs2-servlet-initializer-v2-jakarta</artifactId>
        <version>2.1.10</version>
    </dependency>

However, this will not be sufficient as the webapp is still using an unsupported jackson version, which does not support the jakarta namespace (see GitHub):

(*) NOTE! JAX-RS is the "old" API defined under javax.ws.rs; in 2019 or so, Oracle decided to force a forking of this into "Jakarta" variant under jakarta.ws.ws. As of 2021 most frameworks still use the old API but if you do need/want to use newer one, check out Jakarta-RS provider repo at jackson-jakarta-rs-providers

You have to switch to jackson-jakarta-rs-providers by replacing the jackson dependency in your project with

    <dependency>
        <groupId>com.fasterxml.jackson.jakarta.rs</groupId>
        <artifactId>jackson-jakarta-rs-json-provider</artifactId>
        <version>2.13.0-rc2</version>
    </dependency>

After applying these changes, the webapp will still throw another ClassNotFoundException. Using mvn dependency:tree, we can see, that the updated swagger-jaxrs2-jakarta pulls in a jackson-jaxrs-provider dependency. Odd, isn't it? So we need to exclude it:

    <dependency>
        <groupId>io.swagger.core.v3</groupId>
        <artifactId>swagger-jaxrs2-jakarta</artifactId>
        <version>2.1.10</version>
        <exclusions>
            <exclusion>
                <groupId>com.fasterxml.jackson.jaxrs</groupId>
                <artifactId>jackson-jaxrs-json-provider</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

After that change, we can give it another try by running mvn clean install and mvn tomee:run. The log output logs good and shows the deployed endpoints:

31-Aug-2021 14:46:20.894 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints REST Application: http://localhost:8282/sample                          -> fr.theogiraudet.swagger.Swagger@f25176a
31-Aug-2021 14:46:20.903 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints      Service URI: http://localhost:8282/sample/openapi                  -> Pojo io.swagger.v3.jaxrs2.integration.resources.AcceptHeaderOpenApiResource
31-Aug-2021 14:46:20.903 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8282/sample/openapi                  ->      Response getOpenApiJson(HttpHeaders, UriInfo) throws Exception
31-Aug-2021 14:46:20.903 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8282/sample/openapi                  ->      Response getOpenApiYaml(HttpHeaders, UriInfo) throws Exception
31-Aug-2021 14:46:20.905 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints      Service URI: http://localhost:8282/sample/openapi.{type:json|yaml} -> Pojo io.swagger.v3.jaxrs2.integration.resources.OpenApiResource            
31-Aug-2021 14:46:20.905 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8282/sample/openapi.{type:json|yaml} ->      Response getOpenApi(HttpHeaders, UriInfo, String) throws Exception
31-Aug-2021 14:46:20.905 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints      Service URI: http://localhost:8282/sample/pianos                   -> Pojo fr.theogiraudet.rest.PianoResource                                    
31-Aug-2021 14:46:20.905 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints            DELETE http://localhost:8282/sample/pianos                   ->      Response deleteAllPianos()    
31-Aug-2021 14:46:20.906 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8282/sample/pianos                   ->      Response getAllPianos(UriInfo)
31-Aug-2021 14:46:20.906 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints               GET http://localhost:8282/sample/pianos/{id}              ->      Response getPiano(int)        
31-Aug-2021 14:46:20.906 INFORMATION [main] org.apache.openejb.server.cxf.rs.CxfRsHttpListener.logEndpoints              POST http://localhost:8282/sample/pianos                   ->      Response postPiano(PianoData) 
31-Aug-2021 14:46:20.956 INFORMATION [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Deployment of web application archive [/home/zowallar/IdeaProjects/PausePiano-Backend/target/apache-tomee/webapps/ROOT.war] has finished in [4.433] ms
31-Aug-2021 14:46:20.968 INFORMATION [main] org.apache.catalina.core.StandardContext.setClassLoaderProperty Unable to set the web application class loader property [clearReferencesRmiTargets] to [true] as the property does not exist.
31-Aug-2021 14:46:20.969 INFORMATION [main] org.apache.catalina.core.StandardContext.setClassLoaderProperty Unable to set the web application class loader property [clearReferencesObjectStreamClassCaches] to [true] as the property does not exist.
31-Aug-2021 14:46:20.969 INFORMATION [main] org.apache.catalina.core.StandardContext.setClassLoaderProperty Unable to set the web application class loader property [clearReferencesObjectStreamClassCaches] to [true] as the property does not exist.
31-Aug-2021 14:46:20.969 INFORMATION [main] org.apache.catalina.core.StandardContext.setClassLoaderProperty Unable to set the web application class loader property [clearReferencesThreadLocals] to [true] as the property does not exist.
31-Aug-2021 14:46:20.989 INFORMATION [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Starting ProtocolHandler ["http-nio-8282"]
31-Aug-2021 14:46:21.003 INFORMATION [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Server startup in [4564] milliseconds

However, invoking a related endpoints leads to another exception:

jakarta.servlet.ServletException: Servlet.init() for servlet [fr.theogiraudet.swagger.Swagger] threw exception
    org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:45)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.tomee.catalina.OpenEJBSecurityListener$RequestCapturer.invoke(OpenEJBSecurityListener.java:97)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1696)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:829)

caused by

java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
    org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:248)
    org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:195)
    org.glassfish.jersey.server.ResourceConfig.register(ResourceConfig.java:428)
    org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:306)
    org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:154)
    org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:347)
    jakarta.servlet.GenericServlet.init(GenericServlet.java:158)
    org.apache.tomee.catalina.OpenEJBValve.invoke(OpenEJBValve.java:45)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:543)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.tomee.catalina.OpenEJBSecurityListener$RequestCapturer.invoke(OpenEJBSecurityListener.java:97)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:870)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1696)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:829)

Looking into the log reveals some issues with jackson:

java.lang.NoClassDefFoundError: com/fasterxml/jackson/module/jaxb/JaxbAnnotationIntrospector

Summary

It looks like, that the related tooling (swagger) in combination with jackson is not yet ready to fully work with the jakarta namespace. if you are not torn to use 9, you could simply switch to 8 with working tooling (as 9 only contains namespace changes).

Upvotes: 2

Related Questions