Payara
Payara copied to clipboard
Invalid HTTP method: PATCH when calling Rest Webservices out of Microprofile Rest Client / FISH-1170
Description
I call a webservice implementing HTTP patch from the Microprofile Rest Client. When using the MP Rest Client I get the following error:
javax.ws.rs.ProcessingException: java.net.ProtocolException: Invalid HTTP method: PATCH
Expected Outcome
I would expect that in addition to PUT, DELETE, POST and GET, PATCH can also be used
Current Outcome
As already described, I implement an MP Rest Client that calls a web service that provides the resource as an HTTP PATCH
@RegisterRestClient
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@ClientHeaderParam(name = "Authorization", value = "{generateAuthHeader}")
@RequestScoped
public interface UserResourceService {
@PATCH
@Consumes(MediaType.APPLICATION_JSON)
@Path("/v1/user/{id}")
Response update(@PathParam("id") Integer id, UserRequestDto userRequestDto);
default String generateAuthHeader() {
return ...
}
}
Calling this results in following exception
16:04:00.352 [http-thread-pool::http-listener(5):115] ERROR de.xx.prototype.jsf.domain.user.UserController - Error calling update user.
javax.ws.rs.ProcessingException: java.net.ProtocolException: Invalid HTTP method: PATCH
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:261) ~[jersey-client.jar:?]
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:296) ~[jersey-client.jar:?]
at org.glassfish.jersey.client.JerseyInvocation.lambda$invoke$0(JerseyInvocation.java:609) ~[jersey-client.jar:?]
at org.glassfish.jersey.internal.Errors.process(Errors.java:292) ~[jersey-common.jar:?]
at org.glassfish.jersey.internal.Errors.process(Errors.java:274) ~[jersey-common.jar:?]
at org.glassfish.jersey.internal.Errors.process(Errors.java:205) ~[jersey-common.jar:?]
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:390) ~[jersey-common.jar:?]
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:608) ~[jersey-client.jar:?]
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:421) ~[jersey-client.jar:?]
at org.glassfish.jersey.microprofile.restclient.MethodModel.synchronousCall(MethodModel.java:228) ~[jersey-mp-rest-client.jar:?]
at org.glassfish.jersey.microprofile.restclient.MethodModel.invokeMethod(MethodModel.java:203) ~[jersey-mp-rest-client.jar:?]
at org.glassfish.jersey.microprofile.restclient.InterceptorInvocationContext.proceed(InterceptorInvocationContext.java:110) ~[jersey-mp-rest-client.jar:?]
at org.glassfish.jersey.microprofile.restclient.RestClientModel.invokeMethod(RestClientModel.java:91) ~[jersey-mp-rest-client.jar:?]
at org.glassfish.jersey.microprofile.restclient.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:70) ~[jersey-mp-rest-client.jar:?]
at com.sun.proxy.$Proxy381.update(Unknown Source) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38) ~[weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106) ~[weld-osgi-bundle.jar:3.1.4.Final]
at de.xx.prototype.jsf.domain.user.UserResourceService$1578882783$Proxy$_$$_WeldClientProxy.update(Unknown Source) ~[weld-osgi-bundle.jar:?]
at de.xx.prototype.jsf.domain.user.UserController.updateUser(UserController.java:57) [UserController.class:?]
at de.xx.prototype.jsf.domain.user.UserController$Proxy$_$$_WeldSubclass.updateUser$$super(Unknown Source) [UserController.class:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:51) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:78) [weld-osgi-bundle.jar:3.1.4.Final]
at fish.payara.appserver.cdi.auth.roles.RolesPermittedInterceptor.method(RolesPermittedInterceptor.java:137) [cdi-auth-roles.jar:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:73) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:84) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:72) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:79) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68) [weld-osgi-bundle.jar:3.1.4.Final]
at de.xx.prototype.jsf.domain.user.UserController$Proxy$_$$_WeldSubclass.updateUser(Unknown Source) [UserController.class:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at com.sun.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:163) [jakarta.el.jar:3.0.3.payara-p2]
at com.sun.el.parser.AstValue.invoke(AstValue.java:261) [jakarta.el.jar:3.0.3.payara-p2]
at com.sun.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:237) [jakarta.el.jar:3.0.3.payara-p2]
at org.jboss.weld.module.web.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:40) [weld-osgi-bundle.jar:3.1.4.Final]
at org.jboss.weld.module.web.el.WeldMethodExpression.invoke(WeldMethodExpression.java:50) [weld-osgi-bundle.jar:3.1.4.Final]
at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:65) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.application.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:66) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.application.ActionListenerImpl.getNavigationOutcome(ActionListenerImpl.java:82) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:71) [jakarta.faces.jar:2.3.14.payara-p2]
at javax.faces.component.UICommand.broadcast(UICommand.java:222) [jakarta.faces.jar:2.3.14.payara-p2]
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:847) [jakarta.faces.jar:2.3.14.payara-p2]
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1396) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:58) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:76) [jakarta.faces.jar:2.3.14.payara-p2]
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:177) [jakarta.faces.jar:2.3.14.payara-p2]
at javax.faces.webapp.FacesServlet.executeLifecyle(FacesServlet.java:707) [jakarta.faces.jar:2.3.14.payara-p2]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:451) [jakarta.faces.jar:2.3.14.payara-p2]
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1636) [web-core.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:331) [web-core.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:211) [web-core.jar:?]
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71) [log4j-web-2.14.0.jar:2.14.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:253) [web-core.jar:?]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:211) [web-core.jar:?]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:257) [web-core.jar:?]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [web-core.jar:?]
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:757) [web-core.jar:?]
at org.apache.catalina.core.StandardPipeline.doChainInvoke(StandardPipeline.java:581) [web-core.jar:?]
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97) [web-glue.jar:?]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:158) [web-core.jar:?]
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371) [web-core.jar:?]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238) [web-core.jar:?]
at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520) [kernel.jar:?]
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217) [kernel.jar:?]
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569) [nucleus-grizzly-all.jar:?]
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549) [nucleus-grizzly-all.jar:?]
at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH
at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:487) ~[?:?]
at sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:570) ~[?:?]
at sun.net.www.protocol.https.HttpsURLConnectionImpl.setRequestMethod(HttpsURLConnectionImpl.java:370) ~[?:?]
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:319) ~[jersey-client.jar:?]
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:259) ~[jersey-client.jar:?]
... 95 more
I found similar problems here: https://github.com/eclipse/microprofile-rest-client/issues/154
and this article describing to use SET_METHOD_WORKAROUND https://stackoverflow.com/questions/22355235/patch-request-using-jersey-client
Client jerseyClient = ClientBuilder.newClient().property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
We are currently converting our old Jersey Rest Clients to the MP Rest Client. Before we had the same problem with the Jersey Client, but we were able to use the "SET_METHOD_WORKAROUND" successfully.
Could it be that the MP Rest Client implementation from Payara has to offer this too? Because if I got it right, Payara uses the Jersey Libs.
Steps to reproduce (Only for bug reports)
1 -** Build simple Webservice with HTTP PATCH Method
@PATCH
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateUser(@PathParam("id") Integer id, @RequestBody(content = @Content(mediaType = MediaType.APPLICATION_JSON)) UserRequestDto user) {
...
}
2 -** Build MP Rest Client like described above
3 -** Make changes to the domain.xml configuration:
Start Payara and call Webservice.
Environment
- Payara Version: 5.2020.6
- Edition: Micro
- JDK Version: OpenJDK 11
- Operating System: Linux
Hi @LarsBrenker,
The default JAX-RS client does not support the PATCH method, because HttpClientConnection
does not support it, which by extension means neither does HttpsUrlConnection
. The StackOverflow thread you shared, a user reported due to this, SET_METHOD_WORKAROUND
will not work.
Thank you, Alan
Hello @AlanRoth ,
First of all, thank you very much for your quick reply.
I understand Jersey does not offer a method for HTTP PATCH. I also understood that for this reason Payara does not implement HTTP PATCH in the Microprofile Rest Client Implementation.
Regarding your statement that there is a comment in the Stackoverflow article that the Jersey Workaround is not working, I have to say that is not correct. Below I'll show you that we use this within Payara and that it works wonderfully. Also with Auhorization ...
public void updateUserJersey() {
UserRequestDto userRequestDto = new UserRequestDto();
userRequestDto.setName("Lars");
Response response = ClientBuilder.newClient()
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
.target("https://someUrl")
.path("/v1/user/1")
.request(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + tokenService.getToken())
.build("PATCH", Entity.entity(userRequestDto, MediaType.APPLICATION_JSON)).invoke();
LOGGER.info("updateUser with Jersey Workaround {}", response.getStatus());
}
The only thing ugly is that these illegal reflective access logs are coming. But it works.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.glassfish.jersey.client.internal.HttpUrlConnector$3 (file:/tmp/payaramicro-rt14541380041596282850tmp/runtime/jersey-client.jar) to field sun.net.www.protocol.https.HttpsURLConnectionImpl.delegate
WARNING: Please consider reporting this to the maintainers of org.glassfish.jersey.client.internal.HttpUrlConnector$3
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
The real problem why it wasn't implemented is that there was a bug in Java 8. Since the bug was fixed it is possible to work with this workaround.
So in my opinion, it is the responsibility of the libraries to offer this. Because PATCH is a valid HTTP method and can be used for restful webservices.
I understand Payara is argued that Jersey's job is to make this happen. On the other hand, I am a Payara user. I use the Payara Microprofile Rest Client API. As a user, I don't care what happens in the background at first. As a user, I expect a rest client to support all HTTP methods. Otherwise is not a full-fledged residual client for me.
Should I tell the developers that they can use the Payara Rest Client for GET, POST, PUT and DELETE but should implement this manually with the workaround for PATCH? What will happen? I'm looking for a residual client who can do everything.
My wish for a full-fledged rest client is that it also offers PATCH. As a user, I don't care what he has to do in the background, as long as there is a possibility.
I know that as users of the Community Edition we have no claim to fix this. I can only ask you to think twice about implementing it. But please think about whether, as a provider of an MP Rest Client, it is your claim to also offer PATCH.
If Jersey does not eliminate the illegal reflective access in the future, the workaround will no longer work either. Either they find another option or Payara should use another client.
Perhaps, as soon as the Payara only supports Java 11 and higher, the HTTP client integrated in Java 11 can help. This also works wonderfully with PATCH, even if it does not offer its own method for this. Nevertheless, I don't have to use a workaround like with Jersey and I don't get any illigal reflective calls.
public void updateUserJava11() {
UserRequestDto userRequestDto = new UserRequestDto();
userRequestDto.setName("Lars");
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://someUrl/v1/user/1"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + tokenService.getToken())
.method("PATCH", HttpRequest.BodyPublishers.ofString(JsonbBuilder.create().toJson(userRequestDto)))
.build();
HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.discarding());
LOGGER.info("updateUserJersey 11 {}", response.statusCode());
} catch (URISyntaxException | IOException | InterruptedException e) {
e.printStackTrace();
}
}
We should patch this upstream in Jersey MP Rest Client if the JDK 8 bug is fixed
Hi @LarsBrenker, I have created FISH-1170
, as @smillidge said, we will patch upstream Jersey MP Rest Client, however, we can not guarantee over the timescale that this will occur over. Thank you.