-
Bug
-
Resolution: Done
-
Blocker
-
3.0.4.Final
-
None
-
None
INTRODUCTION
This RestEeasy Test Program demonstrates issues with the RestEasy Client Framework.
GETTING IT WORKING
This is a small, multi-module Maven project that contains a Client and Server.
First, you need to build everything, so at the top level (resteasy-client-test), run: mvn clean install
Second, you will need to run the Server, so at the resteasy-server level, run: mvn jetty:run
Finally, you need to run the Client, so at the resteasy-client level, run: mvn exec:java
WHAT DOES IT DEMONSTRATE?
The main issue that this is designed to highlight is the fact that the Resteasy Client Proxy does not handle an exception well from the server. It essentially renders the Client connection unusable and impairs further interaction with the server. This could be catastrophic to an application, hence it is important to raise this concern so that the Resteasy project can devise a solution.
The Client Tester has a standard test where it will ask the Server for a response (essentially an echo). A test will make a number of attempts and each odd request will cause an Exception at the Server. The main difference between the tests is how the Client is created, and what parameters are used.
The Client Tester runs 3 Test Scenarios against the simple Server:
- Test 1: Using a standard Client (from ClientBuilder.newClient()), makes 4 attempts
- Test 2: Using a pooled Client with a maximum of 3 connections, makes 5 attempts
- Test 3: Using a pooled Client with a maximum of 1 connection, makes 3 attempts
These scenarios seemed enough to illustrate what is going on, but feel free to tinker with your own scenarios.
TEST 1
We have a plain, vanilla, single connection Client.
The first attempt is successful.
The second attempt generates an exception (on the server). The Exception stack trace is:
1647 [ClientTester.main()] ERROR ClientTester - Request threw an exception! javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:170) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:135) at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:58) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:62) at com.sun.proxy.$Proxy30.getContent(Unknown Source) at ClientTester.testMyResource(ClientTester.java:28) at ClientTester.main(ClientTester.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297) at java.lang.Thread.run(Thread.java:680)
The third attempt generates another exception – this time because the Client object is in a bad state. The Exception stack trace is:
1649 [ClientTester.main()] ERROR ClientTester - Request threw an exception! javax.ws.rs.ProcessingException: Unable to invoke request at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:249) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:388) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:102) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:62) at com.sun.proxy.$Proxy30.getContent(Unknown Source) at ClientTester.testMyResource(ClientTester.java:28) at ClientTester.main(ClientTester.java:47) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297) at java.lang.Thread.run(Thread.java:680) Caused by: java.lang.IllegalStateException: Invalid use of BasicClientConnManager: connection still allocated. Make sure to release the connection before allocating another one. at org.apache.http.impl.conn.BasicClientConnectionManager.getConnection(BasicClientConnectionManager.java:161) at org.apache.http.impl.conn.BasicClientConnectionManager$1.getConnection(BasicClientConnectionManager.java:138) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:455) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805) at org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine.invoke(ApacheHttpClient4Engine.java:245) ... 12 more
The fourth, and final, attempt yields the same exception as the last attempt. All future requests are now impossible because the Client is in a bad state.
The expected behavior is that the second attempt would not render the Client inoperable so that the third and future attempts would work.
TEST 2
We have a pooled Client with a maximum of 3 connections, and we make 5 attempts.
The first attempt is successful.
The second attempt generates an exception (on the server). The Exception stack trace is:
javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:170) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:135) at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:58) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:62) at com.sun.proxy.$Proxy30.getContent(Unknown Source) at ClientTester.testMyResource(ClientTester.java:28) at ClientTester.main(ClientTester.java:51) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297) at java.lang.Thread.run(Thread.java:680)
The third attempt is successful.
The fourth attempt generates an exception (on the server). The Exception stack trace is the same as the second attempt.
(Note that we now have 2 bad connections in the client pool.)
The fifth attempt is successful – we still have 1 good connection available in the pool.
This test creates a pool with 2 bad connections; more attempts will render the pool completely useless.
The expected behavior is that the second attempt would not render the Client inoperable.
TEST 3
We have a pooled Client with a maximum of 1 connection, and we try to make 3 attempts.
The first attempt is successful.
The second attempt generates an exception (on the server). The Exception stack trace is:
5792 [ClientTester.main()] ERROR ClientTester - Request threw an exception! javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.handleErrorStatus(ClientInvocation.java:170) at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:135) at org.jboss.resteasy.client.jaxrs.internal.proxy.extractors.BodyEntityExtractor.extractEntity(BodyEntityExtractor.java:58) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:104) at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:62) at com.sun.proxy.$Proxy30.getContent(Unknown Source) at ClientTester.testMyResource(ClientTester.java:28) at ClientTester.main(ClientTester.java:55) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297) at java.lang.Thread.run(Thread.java:680)
The third attempt is now stuck! No exception... Just stuck!
The expected behavior is that the second attempt would not render the Client inoperable so that the third attempt would work.