【问题标题】:Tomee 9 with jax-rs always returns error 404 not found带有 jax-rs 的 Tomee 9 总是返回错误 404 not found
【发布时间】:2021-10-14 08:46:38
【问题描述】:

我正在尝试使用 Java 11、jax-rs 和 Tomee Plume 9 为服务器创建一个简单的 Rest API。
经过javax.Xjakarta.X 之间的多次冲突,我终于能够编译该项目。 但是当我尝试在 Intellij Idea 上运行它时,我的所有请求都出现 404 错误...... 我在日志中没有错误。

这是我的 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>

还有我的 Rest 资源的摘录,位于包 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();
    }
}

我的请求:GET - http://localhost:8080/api/pianos
应用程序上下文是/
在服务器启动时,战争似乎已经很好地定位和部署,如日志所示:

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

如果您需要有关代码的更多信息(如 pom.xml),可以在此处找到该项目: https://github.com/Pause-Piano/PausePiano-Backend

【问题讨论】:

标签: java jakarta-ee jax-rs apache-tomee


【解决方案1】:

tl;博士

您使用的swagger 版本不支持jakarta 命名空间。您必须升级到2.1.7 或更高版本,并将相关的-jakarta 后缀添加到工件描述符中。此外,您使用的是不受支持的 jackson,它也不支持 jakarta。你必须切换到jackson-jakarta-rs-providers(详见下文)。

详细解释

为了重现部署过程,我快速将以下插件配置添加到您的 pom.xml 中,以便通过 tomee:run 在 Maven 中运行 TomEE

        <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>

然后它会尝试部署您的 webapp,它会在控制台输出中打印一些 ClassNotFoundExceptions,表明 javaxjakarta 命名空间之间存在一些麻烦。

您使用的swagger 版本不支持jakarta 命名空间。 official repository on GitHub 声明:

注意:自 2.1.7 版以来,Swagger Core 还支持 Jakarta 命名空间,带有一组带有 -jakarta 后缀的并行工件, 提供与“标准”javax 命名空间相同的功能 那些。详情请查看维基

这意味着,您必须将代码中的相关工件和后缀升级为:

    <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>

但是,这还不够,因为 web 应用程序仍在使用不受支持的 jackson 版本,它不支持 jakarta 命名空间(请参阅 GitHub):

(*) 注意! JAX-RS 是 javax.ws.rs 下定义的“旧”API; 2019年 左右,甲骨文决定将其分叉成“雅加达” jakarta.ws.ws 下的变体。截至 2021 年,大多数框架仍使用 旧 API,但如果您确实需要/想要使用较新的 API,请查看 Jakarta-RS jackson-jakarta-rs-providers 的供应商回购

你必须切换到jackson-jakarta-rs-providers,将项目中的jackson依赖替换为

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

应用这些更改后,webapp 仍会抛出另一个ClassNotFoundException。使用mvn dependency:tree,我们可以看到,更新后的swagger-jaxrs2-jakarta 引入了jackson-jaxrs-provider 依赖项。奇怪,不是吗?所以我们需要排除它:

    <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>

更改后,我们可以通过运行mvn clean installmvn tomee:run 再试一次。日志输出记录良好并显示已部署的端点:

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

但是,调用相关端点会导致另一个异常:

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)

引起

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)

查看日志会发现jackson 存在一些问题:

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

总结

看起来,与 jackson 结合使用的相关工具 (swagger) 尚未准备好完全使用 jakarta 命名空间。如果您不习惯使用 9,您可以使用工作工具简单地切换到 8(因为 9 仅包含命名空间更改)。

【讨论】:

  • 非常感谢您的所有解释!
猜你喜欢
  • 2012-08-16
  • 2015-06-09
  • 2016-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-09
  • 2018-12-16
  • 1970-01-01
相关资源
最近更新 更多