Sheldon Warkentin
Sheldon Warkentin

Reputation: 1746

Interrupt a long running Jersey Client operation

I am using the Oracle Jersey Client, and am trying to cancel a long running get or put operation.

The Client is constructed as:

JacksonJsonProvider provider = new JacksonJsonProvider(new ObjectMapper());
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getSingletons().add(provider);
Client client = Client.create(clientConfig);

The following code is executed on a worker thread:

File bigZipFile = new File("/home/me/everything.zip");

WebResource resource = client.resource("https://putfileshere.com");
Builder builder = resource.getRequestBuilder();

builder.type("application/zip").put(bigZipFile); //This will take a while!

I want to cancel this long-running put. When I try to interrupt the worker thread, the put operation continues to run. From what I can see, the Jersey Client makes no attempt to check for Thread.interrupted().

I see the same behavior when using an AsyncWebResource instead of WebResource and using Future.cancel(true) on the Builder.put(..) call.

So far, the only solution I have come up with to interrupt this is throwing a RuntimeException in a ContainerListener:

client.addFilter(new ConnectionListenerFilter(
  new OnStartConnectionListener(){
    public ContainerListener onStart(ClientRequest cr) {
      return new ContainerListener(){
        public void onSent(long delta, long bytes) {

          //If the thread has been interrupted, stop the operation
          if (Thread.interrupted()) {
            throw new RuntimeException("Upload or Download canceled");
          }

          //Report progress otherwise

        }
      }...    

I am wondering if there is a better solution (perhaps when creating the Client) that correctly handles interruptible I/O without using a RuntimeException.

Upvotes: 5

Views: 1533

Answers (2)

Ironluca
Ironluca

Reputation: 3762

As of Jersey 2.35, the above API has changed. A timeout has been introduces in the client builder which can set read timeout. If the server takes too long to respond, the underlying socket will timeout. However, if the server starts sending the response, it shall not timeout. This can be utilized, if the server does not start sending partial response, which depends on the server implementation.

            client=(JerseyClient)JerseyClientBuilder
                                .newBuilder()
                                .connectTimeout(1*1000, TimeUnit.MILLISECONDS)
                                .readTimeout(5*1000, TimeUnit.MILLISECONDS).build()

The current filters and interceptors are for data only and the solution posted in the original question will not work with filters and interceptors (though I admit I may have missed something there).

Another way is to get hold of the underlying HttpUrlConnection (for standard Jersey client configuration) and it seems to be possible with org.glassfish.jersey.client.HttpUrlConnectorProvider

        HttpUrlConnectorProvider httpConProvider=new HttpUrlConnectorProvider();
        httpConProvider.connectionFactory(new CustomHttpUrlConnectionfactory());

public static class CustomHttpUrlConnectionfactory implements 
HttpUrlConnectorProvider.ConnectionFactory{

    @Override
    public HttpURLConnection getConnection(URL url) throws IOException {
        
        System.out.println("CustomHttpUrlConnectionfactory ..... called");
        
        return (HttpURLConnection)url.openConnection();
        
    }//getConnection closing

}//inner-class closing

I did try the connection provider approach, however, I could not get that working. The idea would be to keep reference to the connection by some means (thread id etc.) and close it if the communication is taking too long. The primary problem was I could not find a way to register the provider with the client. The standard

.register(httpConProvider)

mechanism does not seem to work (or perhaps it is not supposed to work like that) and the documentation is a bit sketchy in that direction.

Upvotes: 0

Gray
Gray

Reputation: 116918

I am wondering if there is a better solution (perhaps when creating the Client) that correctly handles interruptible I/O without using a RuntimeException.

Yeah, interrupting the thread will only work if the code is watching for the interrupts or calling other methods (such as Thread.sleep(...)) that watch for it.

Throwing an exception out of listener doesn't sound like a bad idea. I would certainly create your own RuntimeException class such as TimeoutRuntimeException or something so you can specifically catch and handle it.

Another thing to do would be to close the underlying IO stream that is being written to which would cause an IOException but I'm not familiar with Jersey so I'm not sure if you can get access to the connection.

Ah, here's an idea. Instead of putting the File, how about putting some sort of extension on a BufferedInputStream that is reading from the File but also has a timeout. So Jersey would be reading from the buffer and at some point it would throw an IOException if the timeout expires.

Upvotes: 0

Related Questions