jersey icon indicating copy to clipboard operation
jersey copied to clipboard

Jersey returns 400 Bad Request, logs nothing: option needed to output exceptions to the browser

Open minfrin opened this issue 2 years ago • 26 comments

On an existing long standing application, after an upgrade to tomcat9 and jersey3, attempts to hit the application cause tomcat to return the following response:

<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Ari
al,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .
line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>
Message</b> Bad Request</p><p><b>Description</b> The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed
 request syntax, invalid request message framing, or deceptive request routing).</p><hr class="line" /><h3>Apache Tomcat/9.0.65</h3></body></html>

Jersey needs a property that can be passed to the JDK that causes Jersey to output full stacktraces out of the front door for forensic debugging.

Server side logging is impossible to configure without already knowing what the problem is.

minfrin avatar Nov 27 '23 22:11 minfrin

which is the exact version of Jersey You've upgraded to?

senivam avatar Nov 28 '23 05:11 senivam

You cannot use Jersey 3 with Tomcat 9, you need Tomcat 10. See https://tomcat.apache.org/download-10.cgi.

jansupol avatar Nov 28 '23 09:11 jansupol

which is the exact version of Jersey You've upgraded to?

Jersey v2.39.1, also tried v2.40.

Sorry the original description above said jersey3, that was wrong :(

minfrin avatar Nov 28 '23 10:11 minfrin

It looks like the point where the bad request originates is here:

https://github.com/eclipse-ee4j/jersey/blob/8105415f9a6356de774c8ffe6ab5dfa77934b69f/media/jaxb/src/main/java/org/glassfish/jersey/jaxb/internal/AbstractRootElementJaxbProvider.java#L116

There is an underlying unmarshalling problem, JAXB is broken, this bubbles up to the above. The exception is then hidden.

minfrin avatar Nov 28 '23 10:11 minfrin

Is the exception printed in the Tomcat server log?

jansupol avatar Nov 28 '23 14:11 jansupol

Unfortunately tomcat logging is of no use, because you need to know what the exception is to tell tomcat to log it, but without knowing what the exception is, you can't tell tomcat what to log.

I eventually stopped it in a debugger by breakpointing on all exceptions.

For some reason jersey was trying to use an Oracle SAX parser that wasn't being pulled in transitively.

java.lang.ClassNotFoundException: oracle.xml.jaxp.JXSAXParserFactory

Providing the missing jar above also didn't work, I don't remember why, I've been going round in circles and I've forgotten what I tried.

I tried to upgrade all of JAXB to jakarta.xml across the board, but it appears that jersey v2.41 doesn't support the jakarta version of JAXB.

Does a matrix exist anywhere of what versions of jersey is compatible with what? I am aware that breaking changes were made in the v2.x series, and I cannot for the life of me find a combination of jars (jersey, jaxb, persistence) that works.

I am stuck on tomcat9, that cannot change. I understand that means I am stuck on jersey v2.41, that cannot change. What is compatible with jersey v2.41?

minfrin avatar Nov 28 '23 14:11 minfrin

Jersey 2.x works with javax namespace, so whatever jakarta jar contains javax namespace (such as jaxb 2.3.2), Jersey supports it.

jansupol avatar Nov 28 '23 14:11 jansupol

What is confusing me horribly is that the POM of jersey-media-jaxb will tell me it depends on jakarta.xml.bind-api, but exceptions thrown by jersey are telling me that javax.xml APIs are missing.

https://eclipse-ee4j.github.io/jersey.github.io/project-info/2.41/jersey/project/jersey-media-jaxb/dependencies.html

I am now getting jersey telling me that application/xml is 415 – Unsupported Media Type in code that receives XML.

minfrin avatar Nov 28 '23 14:11 minfrin

Yes, it is confusing. First, there was Java EE 8, using javax namespace (compatible with Jersey 2.x) Then, there was Jakarta EE 8, using jakarta prefixed jars, but inside, there is javax package. (Compatible with Jersey 2.x) Then, there was jakarta EE 9 and 10, using jakarta prefixed jars, but inside, there is jakarta package (Not compatible with Jersey 2.x)

jansupol avatar Nov 28 '23 15:11 jansupol

Jersey 2.x works with javax namespace, so whatever jakarta jar contains javax namespace (such as jaxb 2.3.2), Jersey supports it.

Can you confirm the exact maven coordinates of jaxb 2.3.2 if possible? this version doesn't appear to exist (v2.3.1 exists).

minfrin avatar Nov 28 '23 15:11 minfrin

Then, there was Jakarta EE 8, using jakarta prefixed jars, but inside, there is javax package. (Compatible with Jersey 2.x)

Are there any maven coordinates for jakarta prefixed jars with javax packages inside? The inconsistent naming makes this impossible to search for.

minfrin avatar Nov 28 '23 15:11 minfrin

This is the maven coordinates I am using right now:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ roxanne ---
[INFO] com.example:example:war:2.0.0-SNAPSHOT
[INFO] +- javax.ws.rs:javax.ws.rs-api:jar:2.1.1:compile
[INFO] +- org.glassfish.jersey.containers:jersey-container-servlet:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-common:jar:2.41:compile
[INFO] |  |  \- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-server:jar:2.41:compile
[INFO] |  |  \- org.glassfish.jersey.core:jersey-client:jar:2.41:compile
[INFO] |  \- jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-moxy:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.ext:jersey-entity-filtering:jar:2.41:compile
[INFO] |  +- jakarta.json:jakarta.json-api:jar:1.1.6:compile
[INFO] |  +- org.glassfish:jakarta.json:jar:1.1.6:compile
[INFO] |  \- org.eclipse.persistence:org.eclipse.persistence.moxy:jar:2.7.12:compile
[INFO] |     \- org.eclipse.persistence:org.eclipse.persistence.core:jar:2.7.12:compile
[INFO] |        \- org.eclipse.persistence:org.eclipse.persistence.asm:jar:9.4.0:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-multipart:jar:2.41:compile
[INFO] |  \- org.jvnet.mimepull:mimepull:jar:1.9.15:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-jaxb:jar:2.41:compile
[INFO] |  +- org.glassfish.hk2.external:jakarta.inject:jar:2.6.1:compile
[INFO] |  \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.3:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:2.41:compile
[INFO] |  +- org.glassfish.hk2:hk2-locator:jar:2.6.1:compile
[INFO] |  |  +- org.glassfish.hk2.external:aopalliance-repackaged:jar:2.6.1:compile
[INFO] |  |  +- org.glassfish.hk2:hk2-api:jar:2.6.1:compile
[INFO] |  |  \- org.glassfish.hk2:hk2-utils:jar:2.6.1:compile
[INFO] |  \- org.javassist:javassist:jar:3.29.2-GA:compile
[INFO] +- jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.1:compile
[INFO] |  \- jakarta.activation:jakarta.activation-api:jar:2.1.2:compile
[INFO] +- org.glassfish.jaxb:jaxb-runtime:jar:4.0.4:compile
[INFO] |  \- org.glassfish.jaxb:jaxb-core:jar:4.0.4:compile
[INFO] |     +- org.eclipse.angus:angus-activation:jar:2.0.1:runtime
[INFO] |     +- org.glassfish.jaxb:txw2:jar:4.0.4:compile
[INFO] |     \- com.sun.istack:istack-commons-runtime:jar:4.1.2:compile
[INFO] +- javax.activation:activation:jar:1.1.1:compile
[INFO] +- javax.validation:validation-api:jar:2.0.1.Final:compile
[INFO] +- javax.xml.bind:jaxb-api:jar:2.3.1:compile
[INFO] |  \- javax.activation:javax.activation-api:jar:1.2.0:compile
[INFO] +- com.example:example-db:jar:2.0.0-SNAPSHOT:compile
[INFO] |  +- jakarta.persistence:jakarta.persistence-api:jar:3.1.0:compile
[INFO] |  +- jakarta.validation:jakarta.validation-api:jar:3.0.2:compile
[INFO] |  +- org.datanucleus:datanucleus-accessplatform-jakarta-rdbms:pom:6.0.6:compile
[INFO] |  |  +- org.datanucleus:jakarta.persistence:jar:3.1.0:compile
[INFO] |  |  +- org.datanucleus:datanucleus-core:jar:6.0.6:compile
[INFO] |  |  +- org.datanucleus:datanucleus-api-jakarta:jar:6.0.0-release:compile
[INFO] |  |  +- org.datanucleus:datanucleus-jakarta-query:jar:6.0.0-release:compile
[INFO] |  |  \- org.datanucleus:datanucleus-rdbms:jar:6.0.6:compile
[INFO] |  +- org.apache.commons:commons-lang3:jar:3.12.0:compile
[INFO] |  \- com.example:example-xml:jar:2.0.1-SNAPSHOT:compile
[INFO] +- commons-configuration:commons-configuration:jar:1.10:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] \- javax.servlet:javax.servlet-api:jar:4.0.1:provided

minfrin avatar Nov 28 '23 15:11 minfrin

I would recommend using what Jersey is tested with. Some dependencies (non-Jakarta) break backward compatibility without upgrading its major version. The dependencies are best viewed in the project pom file.

jansupol avatar Nov 28 '23 15:11 jansupol

Interestingly, you have both versions of JAX-B, but you are missing the implementation, I'd recommend com.sun.xml.bind:jaxb-osgi:2.3.8

jansupol avatar Nov 28 '23 15:11 jansupol

Managed to massage things until the dependencies look like this, and I am back to the original 400 Bad Request right at the top.

java.lang.ClassNotFoundException: oracle.xml.jaxp.JXSAXParserFactory

It looks like we're missing an XML parser, but something buried in here is hard coded to look for something from Oracle.

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ example ---
[INFO] com.example:exaple:war:2.0.0-SNAPSHOT
[INFO] +- javax.ws.rs:javax.ws.rs-api:jar:2.1.1:compile
[INFO] +- org.glassfish.jersey.containers:jersey-container-servlet:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-common:jar:2.41:compile
[INFO] |  |  \- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
[INFO] |  +- org.glassfish.jersey.core:jersey-server:jar:2.41:compile
[INFO] |  |  \- org.glassfish.jersey.core:jersey-client:jar:2.41:compile
[INFO] |  \- jakarta.ws.rs:jakarta.ws.rs-api:jar:2.1.6:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-moxy:jar:2.41:compile
[INFO] |  +- org.glassfish.jersey.ext:jersey-entity-filtering:jar:2.41:compile
[INFO] |  +- jakarta.json:jakarta.json-api:jar:1.1.6:compile
[INFO] |  +- org.glassfish:jakarta.json:jar:1.1.6:compile
[INFO] |  +- org.eclipse.persistence:org.eclipse.persistence.moxy:jar:2.7.12:compile
[INFO] |  |  \- org.eclipse.persistence:org.eclipse.persistence.core:jar:2.7.12:compile
[INFO] |  |     \- org.eclipse.persistence:org.eclipse.persistence.asm:jar:9.4.0:compile
[INFO] |  \- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
[INFO] |     \- jakarta.activation:jakarta.activation-api:jar:1.2.2:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-multipart:jar:2.41:compile
[INFO] |  \- org.jvnet.mimepull:mimepull:jar:1.9.15:compile
[INFO] +- org.glassfish.jersey.media:jersey-media-jaxb:jar:2.41:compile
[INFO] |  +- org.glassfish.hk2.external:jakarta.inject:jar:2.6.1:compile
[INFO] |  \- org.glassfish.hk2:osgi-resource-locator:jar:1.0.3:compile
[INFO] +- org.glassfish.jersey.inject:jersey-hk2:jar:2.41:compile
[INFO] |  +- org.glassfish.hk2:hk2-locator:jar:2.6.1:compile
[INFO] |  |  +- org.glassfish.hk2.external:aopalliance-repackaged:jar:2.6.1:compile
[INFO] |  |  +- org.glassfish.hk2:hk2-api:jar:2.6.1:compile
[INFO] |  |  \- org.glassfish.hk2:hk2-utils:jar:2.6.1:compile
[INFO] |  \- org.javassist:javassist:jar:3.29.2-GA:compile
[INFO] +- javax.activation:activation:jar:1.1.1:compile
[INFO] +- javax.validation:validation-api:jar:2.0.1.Final:compile
[INFO] +- com.example:example-db:jar:2.0.0-SNAPSHOT:compile
[INFO] |  +- jakarta.persistence:jakarta.persistence-api:jar:3.1.0:compile
[INFO] |  +- com.sun.xml.bind:jaxb-impl:jar:2.3.8:compile
[INFO] |  |  \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
[INFO] |  +- com.sun.xml.bind:jaxb-osgi:jar:2.3.8:compile
[INFO] |  +- jakarta.validation:jakarta.validation-api:jar:3.0.2:compile
[INFO] |  +- org.datanucleus:datanucleus-accessplatform-jakarta-rdbms:pom:6.0.6:compile
[INFO] |  |  +- org.datanucleus:jakarta.persistence:jar:3.1.0:compile
[INFO] |  |  +- org.datanucleus:datanucleus-core:jar:6.0.6:compile
[INFO] |  |  +- org.datanucleus:datanucleus-api-jakarta:jar:6.0.0-release:compile
[INFO] |  |  +- org.datanucleus:datanucleus-jakarta-query:jar:6.0.0-release:compile
[INFO] |  |  \- org.datanucleus:datanucleus-rdbms:jar:6.0.6:compile
[INFO] |  +- org.hibernate.validator:hibernate-validator:jar:6.2.5.Final:compile
[INFO] |  |  +- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile
[INFO] |  |  \- com.fasterxml:classmate:jar:1.5.1:compile
[INFO] |  +- org.hibernate.validator:hibernate-validator-cdi:jar:6.2.5.Final:compile
[INFO] |  +- org.apache.commons:commons-lang3:jar:3.12.0:compile
[INFO] |  \- com.example:example-xml:jar:1.9.0:compile
[INFO] |     \- javax.xml.bind:jaxb-api:jar:2.3.1:compile
[INFO] |        \- javax.activation:javax.activation-api:jar:1.2.0:compile
[INFO] +- commons-configuration:commons-configuration:jar:1.10:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] \- javax.servlet:javax.servlet-api:jar:4.0.1:provided

minfrin avatar Nov 28 '23 18:11 minfrin

Chasing two bugs here.

The first is whatever in Jersey is triggering the 400 Bad Request, which may be the ClassNotFoundException, but may not be, as other ClassNotFoundExceptions are thrown making reference to classes in tomcat itself.

The second bug is a Tomcat bug, or it might be a Tomcat bug triggered by a Jersey bug, where we arrive at the following code that handles the printout of the 400 error, and there is no throwable (it is null):

https://github.com/apache/tomcat/blob/9.0.x/java/org/apache/catalina/valves/ErrorReportValve.java#L129

No throwable, and an unhandled 400 Bad Request, and we get Tomcat's generic error screen with no exception and nothing logged.

minfrin avatar Nov 28 '23 21:11 minfrin

Found the source of the Oracle XML parser:

https://github.com/eclipse-ee4j/jersey/blob/8105415f9a6356de774c8ffe6ab5dfa77934b69f/core-common/src/main/java/org/glassfish/jersey/internal/util/SaxHelper.java#L41

For some reason jersey has hard coded the name of an Oracle implementation of the class oracle.xml.jaxp.JXSAXParserFactory.

Trying to use this implementation breaks Datanucleus, which only works with Xerces.

Does jersey2 work on JDKs above java8 with all the XML code removed?

minfrin avatar Nov 28 '23 21:11 minfrin

This is interesting, but it only logs information that the class was not found under FINER level logging. That does not break the runtime. You would likely see 500 if an exception would be thrown.

Under the FINER logging level, you should see the message "Starting mapping of the exception." in the server log, followed by the exception. But 400 is often a Tomcat Exception (caused by incorrect libraries in Tomcat), thrown before Jersey has an opportunity to do anything.

jansupol avatar Nov 28 '23 23:11 jansupol

I am also experiencing same kind of problem with Jersey 3 with Tomcat 10.1.18 . Whats happening for me is when requests with empty path parameters are passed 400 bad request is thrown without any logging. Example: for request like this error is thrown. http://localhost:9090/proactive-api/rest/api/deviceManagement/gokul/PHONE///100///0/gokul

But for request like this are passing normally http://localhost:9090/proactive-api/rest/user/getUser/gokul

The main thing is request is not reaching the spring layer. The request is passed from Tomcat without any changes in the path. Only logging I can see is request access log from Tomcat which just shows the request path and error code. Weird thing is this worked fine in Jersey 2 and Tomcat 9.

Gokul-2720 avatar Feb 07 '24 09:02 Gokul-2720

@minfrin I am checking this: https://github.com/gluser1357/jerseyissues/tree/issue-5404-crosscontext

It is working in latest 3.1 (I am testing with the compiled Jersey 3.1 branch that has the version 3.1.99-SNAPSHOT)

$ curl -v http://localhost:8080/jerseyissue-5404/tests/forward
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /jerseyissue-5404/tests/forward HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Type: text/plain
< Content-Length: 17
< Date: Wed, 07 Feb 2024 13:26:32 GMT
< 
* Connection #0 to host localhost left intact
MyAPI /tests/a ok

jbescos avatar Feb 07 '24 13:02 jbescos

This is what I am using in my project <org.springframework.version>6.1.2</org.springframework.version> <spring.security.version>6.2.1</spring.security.version> <jersey.version>3.1.5</jersey.version>

This is the latest available jersey version in maven repository.

This is my configuration in web.xml <servlet> <servlet-name>RestService</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>com.tarshan.gokul.proactive.rest</param-value> </init-param> <init-param> <param-name>jersey.config.jsonFeature.jackson.jaxb.annotationProcessing</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>jersey.config.server.modelValidation</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>RestService</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>

And this is what I am getting in Tomcat localhost access log

127.0.0.1 - - [07/Feb/2024:17:23:06 +0530] "GET /proactive-api/rest/api/deviceManagement/gokul/PHONE///100///0/gokul HTTP/1.1" 400 763

The error I'm getting in Postman is <!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).</p><hr class="line" /><h3>Apache Tomcat/10.1.18</h3></body></html>

Other than these, I dont have any trace or reason for this behaviour. Could you please try testing with // [Consecutive slashes] in your request path like this: /jerseyissue-5404/tests//forward I am passing these consecutive slashes because those are empty path parameters.

Gokul-2720 avatar Feb 08 '24 04:02 Gokul-2720

I am getting 404 in Jersey instead of 400, in this stacktrace:

Daemon Thread [http-nio-8080-exec-16] (Suspended)	
	org.glassfish.jersey.server.ServerRuntime$1.run() line: 258	
	org.glassfish.jersey.internal.Errors$1.call() line: 248	
	org.glassfish.jersey.internal.Errors$1.call() line: 244	
	org.glassfish.jersey.internal.Errors.process(java.util.concurrent.Callable<T>, boolean) line: 292	
	org.glassfish.jersey.internal.Errors.process(org.glassfish.jersey.internal.util.Producer<T>, boolean) line: 274	
	org.glassfish.jersey.internal.Errors.process(java.lang.Runnable) line: 244	
	org.glassfish.jersey.inject.hk2.Hk2RequestScope(org.glassfish.jersey.process.internal.RequestScope).runInScope(org.glassfish.jersey.process.internal.RequestContext, java.lang.Runnable) line: 265	
	org.glassfish.jersey.server.ServerRuntime.process(org.glassfish.jersey.server.ContainerRequest) line: 240	
	org.glassfish.jersey.server.ApplicationHandler.handle(org.glassfish.jersey.server.ContainerRequest) line: 697	
	org.glassfish.jersey.servlet.WebComponent.serviceImpl(java.net.URI, java.net.URI, jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse) line: 394	
	org.glassfish.jersey.servlet.WebComponent.service(java.net.URI, java.net.URI, jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse) line: 346	
	org.glassfish.jersey.servlet.ServletContainer.service(java.net.URI, java.net.URI, jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse) line: 357	
	org.glassfish.jersey.servlet.ServletContainer.service(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse) line: 311	
	org.glassfish.jersey.servlet.ServletContainer.service(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse) line: 205	
	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse) line: 205	
	org.apache.catalina.core.ApplicationFilterChain.doFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse) line: 149	
	org.apache.tomcat.websocket.server.WsFilter.doFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse, jakarta.servlet.FilterChain) line: 51	
	org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse) line: 174	
	org.apache.catalina.core.ApplicationFilterChain.doFilter(jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse) line: 149	
	org.apache.catalina.core.StandardWrapperValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 166	
	org.apache.catalina.core.StandardContextValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 90	
	org.apache.catalina.authenticator.NonLoginAuthenticator(org.apache.catalina.authenticator.AuthenticatorBase).invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 482	
	org.apache.catalina.core.StandardHostValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 115	
	org.apache.catalina.valves.ErrorReportValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 93	
	org.apache.catalina.valves.AccessLogValve(org.apache.catalina.valves.AbstractAccessLogValve).invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 676	
	org.apache.catalina.core.StandardEngineValve.invoke(org.apache.catalina.connector.Request, org.apache.catalina.connector.Response) line: 74	
	org.apache.catalina.connector.CoyoteAdapter.service(org.apache.coyote.Request, org.apache.coyote.Response) line: 341	
	org.apache.coyote.http11.Http11Processor.service(org.apache.tomcat.util.net.SocketWrapperBase<?>) line: 390	
	org.apache.coyote.http11.Http11Processor(org.apache.coyote.AbstractProcessorLight).process(org.apache.tomcat.util.net.SocketWrapperBase<?>, org.apache.tomcat.util.net.SocketEvent) line: 63	
	org.apache.coyote.AbstractProtocol$ConnectionHandler<S>.process(org.apache.tomcat.util.net.SocketWrapperBase<S>, org.apache.tomcat.util.net.SocketEvent) line: 894	
	org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun() line: 1741	
	org.apache.tomcat.util.net.NioEndpoint$SocketProcessor(org.apache.tomcat.util.net.SocketProcessorBase<S>).run() line: 52	
	org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker) line: 1191	
	org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run() line: 659	
	org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() line: 61	
	org.apache.tomcat.util.threads.TaskThread(java.lang.Thread).run() line: 833	

With this resource method:

    @GET
    @Path("paths/{a}/{b}/c")
    public static Response paths(@PathParam("a") String a, @PathParam("b") String b) throws Exception {
        Thread.dumpStack();
        System.out.println("tests/paths/{a}/{b}/c");
        return Response.ok().entity("paths/" + a + "/" + b + "/c").build();
    }
$ curl -v http://localhost:8080/jerseyissue-5404/tests/paths///c
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /jerseyissue-5404/tests/paths///c HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 
< Content-Type: text/html;charset=utf-8
< Content-Language: en
< Content-Length: 713
< Date: Thu, 08 Feb 2024 06:14:04 GMT
< 
* Connection #0 to host localhost left intact
<!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Not Found</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/10.1.8</h3></body></html>
$ curl -v http://localhost:8080/jerseyissue-5404/tests/paths/a/b/c
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /jerseyissue-5404/tests/paths/a/b/c HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Content-Type: text/plain
< Content-Length: 11
< Date: Thu, 08 Feb 2024 06:14:27 GMT
< 
* Connection #0 to host localhost left intact
paths/a/b/c

@Gokul-2720 Could you share a simple app that throws 400?. I am not able to reproduce your issue.

jbescos avatar Feb 08 '24 06:02 jbescos

I dont know what is happening here, If I provide values for all the path parameters like you did, I get 200 response code and API is returning the response properly.

http://localhost:9090/proactive-api/rest/api/deviceManagement/gokul/PHONE/a/b/100/c/d/0/gokul

Tomcat access log: 127.0.0.1 - - [08/Feb/2024:12:50:54 +0530] "GET /proactive-api/rest/api/deviceManagement/gokul/PHONE/a/b/100/c/d/0/gokul HTTP/1.1" 200 46

But when the path parameters are empty I'm getting 400 bad request.

Actually I'm working on a complex enterprise application upgrade. Let me check whether I can provide any simple application with you.

Gokul-2720 avatar Feb 08 '24 09:02 Gokul-2720

In case it helps someone else in pain...

Environment: AdoptOpenJDK Java 1.8.0_212, Tomcat 9.0.91. Objective: upgrade Jackson codehaus -> fasterXML.

One answer others might build off:

Before (codehaus Jackson):

  <dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-server</artifactId>
    <version>1.9</version>
  </dependency>
  <dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.9</version>
  </dependency>
  <servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.eflow.rest.resource</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

After (fasterXML Jackson):

  <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.25.1</version>
  </dependency>
  <dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.25.1</version>
  </dependency>
  <servlet>
    <servlet-name>jersey-serlvet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>com.eflow.rest.resource</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  note: org.glassfish.jersey 2.26-b04 to 2.44 failed "InjectionManagerFactory not found"; 2.25.1 was last "stable"

Comment: I spent over a day on just this, mirroring the pain in this thread, reading commentary with folks like me being insulted all over the internet. You know, my life is about writing real use applications, not mysterious and convoluted relatively undocumented platform configuration. It shouldn't be this hard. And those who spend their life building these platform configurations should not be so cavalier about backwards compatibility and so condescending with their endless "you should have known this and that". If Java is so great, then why is it so painful?

Thank you:

  • https://www.codejava.net/java-ee/web-services/java-crud-restful-web-services-examples-with-jersey-and-tomcat
  • https://stackoverflow.com/questions/26207252/messagebodywriter-not-found-for-media-type-application-json

csmwww avatar Aug 13 '24 20:08 csmwww

Hi @csmwww , thank you for the great investigation, it looks really impressive! Regarding the note about "InjectionManagerFactory not found" - indeed, the API has changed after the version 2.25.1. If you are interested, take a look at the Migrating from Jersey 2.25 to 2.26 chapter from the user guide.

And I have a question - does your investigation resolve this issue and it can be closed, or shall it still be kept open?

senivam avatar Aug 14 '24 05:08 senivam

In short, since 2.26 the org.glassfish.jersey.inject:jersey-hk2 module is required.

jansupol avatar Aug 14 '24 13:08 jansupol