سؤال

I've written a small JAX-WS webservice that I'm running outside a container with Endpoint.publish():

Endpoint endpoint = Endpoint.create(new MyServiceImpl());
endpoint.publish("http://localhost:4425/myService");

If any of my web service methods throws an exception, the endpoint is not gracefully closed and the address remains in use until Windows eventually releases it. This causes the classic error:

com.sun.xml.internal.ws.server.ServerRtException: Server Runtime Error: java.net.BindException: Address already in use: bind

I could wrap all of my web service methods with try/catch, but this seems a little repetitive. I also tried installing a clean-up class via Thread.setDefaultUncaughtExceptionHandler(), but this wasn't triggered when my web service method threw an exception.

Is there a more elegant way of solving this than resorting to countless try/catch blocks?


Based on Waldheinz's answer, I've attempted to use Jetty classes in favour of JDK defaults. The code compiles, but when executed it terminates immediately after publish. When using JDK classes, the main thread would remain alive until I manually terminated the process. Any ideas what's going amiss? I wonder if an exception is happening somewhere but being swallowed so I can't see it.

Endpoint endpoint = Endpoint.create(new MyServiceImpl());

Server s = new Server(new InetSocketAddress(HOST, PORT));
ServerConnector connector = new ServerConnector(s);
connector.setReuseAddress(true);
s.setConnectors(new Connector[] { connector });
s.setHandler(new ContextHandlerCollection());

JettyHttpServer server = new JettyHttpServer(s, false);
JettyHttpContext context = (JettyHttpContext) server.createContext(PATH);
endpoint.publish(context);
هل كانت مفيدة؟

المحلول 2

If the bind fails, but the old instance is not running any more, setting SO_REUSEADDR is likely to help.

نصائح أخرى

Custom thread pool for an endpoint may help:

ThreadFactory factory = new ThreadFactory() {

  @Override
  public Thread newThread(Runnable target) {
    final Thread thread = new Thread(target);
    thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            // put error handling code here
        }

    });
    return thread;
  }


};

ExecutorService executor = Executors.newCachedThreadPool(factory);
Endpoint endpoint = Endpoint.create(new MyServiceImpl());
endpoint.setExecutor(executor);
endpoint.publish("http://localhost:4425/myService");

Using the hint from Waldheinz, I adjusted my web service to use Jetty as follows:

Server s = new Server(PORT);
ServerConnector connector = new ServerConnector(s);
connector.setReuseAddress(true); // avoid bind errors
s.setHandler(new ContextHandlerCollection());
s.setConnectors(new Connector[] { connector });

System.setProperty("com.sun.net.httpserver.HttpServerProvider",
        "org.eclipse.jetty.http.spi.JettyHttpServerProvider");

Endpoint.publish(HOST + ":" + PORT + PATH, new MyServiceImpl());

This seems to be handling the problems well. Bounty points go to Waldheinz for starting me down the right route. Thanks to Jk1 for an alternative suggestion.

I realize an answer's already been accepted, but it's unfortunate that it relies on a Jetty-specific solution. I haven't tried anything I'm about to suggest, but the first thing that comes to mind is to make use of Endpoint.setExecutor(Executor).

One possibility is to create a ThreadFactory that explicitly sets the uncaught exception handler for every thread it creates. Guava's ThreadFactoryBuilder can help:

public class MyHandler implements Thread.UncaughtExceptionHandler {

    private final Endpoint endpoint;

    public MyHandler(Endpoint endpoint) {
        this.endpoint = endpoint;
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // ...
    }
}

Endpoint endpoint = Endpoint.create(new MyServiceImpl());
Thread.UncaughtExceptionHandler handler = new MyHandler(endpoint);
ThreadFactory factory = new ThreadFactoryBuilder().setUncaughtExceptionHandler(handler).build();
Executor executor = Executors.newSingleThreadExecutor(factory);
endpoint.setExecutor(executor);
endpoint.publish("http://localhost:4425/myService");

Another possibility is to extend ThreadPoolExecutor and override afterExecute(Runnable, Throwable).

public class ServiceExecutor extends ThreadPoolExecutor {

    private final Endpoint endpoint;

    // ThreadPoolExecutor has four ctors
    public ServiceExecutor(Endpoint endpoint, ...) {
        this.endpoint = endpoint;
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t != null) {
            // ...
        }
    }
}

Endpoint endpoint = Endpoint.create(new MyServiceImpl());
Executor executor = new ServiceExecutor(endpoint, ...);
endpoint.setExecutor(executor);
endpoint.publish("http://localhost:4425/myService");
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top