How do I interrupt a thread that is blocked in a remote call in Axis 1.4?

If you have ever tried to, you may know that a thread performing a blocking I/O operation cannot be interrupted in Java. The problem lies in the JVM itself, and it comes from the ages of Java 1.1. There are several bugs open in java.sun, and it looks like this behaviour is here to stay.

There are two workarounds for this issue:

Well, porting Axis 1.4 to Java NIO does not look like the way to go so, let's take a look at the second option.

The following is a subclass of HTTPSender that will allow us to close the socket (and hence its associated streams):

package org.apache.axis.transport.http;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.axis.AxisFault;
import org.apache.axis.MessageContext;
import org.apache.axis.components.net.BooleanHolder;

/**
 * HTTPSender modified to allow interruption during I/O read.
 */
public class InterruptibleHttpSender extends HTTPSender {

  private static final ConcurrentMap<Thread, Socket> SOCKETS
    = new ConcurrentHashMap<Thread, Socket>();

  /**
   * Interrupts the specified thread operation by closing its associated socket.
   *
   * @param t The thread to interrupt
   */
  public static void interrupt(Thread t) {
    Socket s = SOCKETS.get(t);
    if (s != null) {
      try {
        s.close();
      } catch (IOException ignore) { }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override public void invoke(MessageContext ctx) throws AxisFault {
    Thread t = Thread.currentThread();
    try {
      super.invoke(ctx);
    } catch (AxisFault fault) {
      Throwable cause = fault.getCause();
      if (cause instanceof SocketTimeoutException) {
        // Special case of InterruptedIOException
        throw fault;
      else if (cause instanceof InterruptedIOException) {
        InterruptedException ie = new InterruptedException();
        ie.setStackTrace(cause.getStackTrace());
        throw AxisFault.makeFault(ie);
      } else if (cause instanceof SocketException && t.isInterrupted()) {
        InterruptedException ie = new InterruptedException();
        ie.setStackTrace(cause.getStackTrace());
        throw AxisFault.makeFault(ie);
      } else {
        throw fault;
      }
    } finally {
      SOCKETS.remove(t);
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override protected void getSocket(SocketHolder sock, MessageContext ctx,
    String protocol, String host, int port, int timeout, StringBuffer otherHeaders,
    BooleanHolder useFullURL) throws Exception {

    super.getSocket(sock, ctx, protocol, host, port,
        timeout, otherHeaders, useFullURL);

    SOCKETS.put(Thread.currentThread(), sock.getSocket());
  }
}

As you can see, I am using Java 5 ConcurrentMap to handle thread-safety. If you need to compile in Java 1.4, it can be replaced by a simple Hashtable.

Now, to put this class to work we need to do two things: register it, and use it :-)

For registering the class, we just need to modify the client-config.wsdd descriptor:

 <!--transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/-->
 <transport name="http" pivot="java:org.apache.axis.transport.http.InterruptibleHttpSender"/>

If this is not an option, there are other ways to change the configuration.

Unfortunately, this class is not magic. The interruption of the thread must be done manually by calling the InterruptibleHttpSender#interrupt(Thread) static method. This call must be done AFTER calling Thread#interrupt(), because it depends on the "isInterrupted" state of the thread to correctly handle exceptions. So, wherever you have a call to Thread.interrupt(), you should do something like:

  Thread t = ...

  t.interrupt();
  InterruptibleHttpSender.interrupt(t);

This step can be partially automated by creating a custom Thread subclass:

public abstract class AbstractAxisClientThread extends Thread {
  /**
   * {@inheritDoc}
   */
  @Override public void interrupt() {
    super.interrupt();
    InterruptibleHttpSender.interrupt(this);
  }
}

and subclassing from it.

FrontPage/Axis/AxisClientConfiguration/InterruptCalls (last edited 2009-09-20 22:48:03 by localhost)