发生异常时如何正常关闭端点?

时间:2014-05-12 13:24:36

标签: java web-services jetty jax-ws

我编写了一个小型JAX-WS Web服务,我在Endpoint.publish()的容器外运行:

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

如果我的任何Web服务方法抛出异常,则端点不会正常关闭,并且该地址将保持使用状态,直到Windows最终释放它为止。这会导致经典错误:

  

com.sun.xml.internal.ws.server.ServerRtException:服务器运行时错误:java.net.BindException:已在使用的地址:bind

我可以使用try / catch包装所有的Web服务方法,但这似乎有点重复。我还尝试通过Thread.setDefaultUncaughtExceptionHandler()安装一个清理类,但是当我的Web服务方法引发异常时,没有触发此类。

有没有更优雅的解决方法,而不是诉诸无数的try / catch块?


根据Waldheinz的回答,我试图使用Jetty类来支持JDK默认值。代码编译,但执行时它会在publish之后立即终止。使用JDK类时,主线程将保持活动状态,直到我手动终止该进程。有什么想法会有什么不妥?我想知道是否有异常发生在某处,但被吞没,所以我看不到它。

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);

4 个答案:

答案 0 :(得分:3)

端点的自定义线程池可能有所帮助:

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");

答案 1 :(得分:2)

如果绑定失败,但旧实例不再运行,则设置SO_REUSEADDR可能会有所帮助。

答案 2 :(得分:1)

使用Waldheinz的提示,我调整了我的Web服务以使用Jetty,如下所示:

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());

这似乎很好地处理了问题。恩惠积分去Waldheinz开始我正确的路线。感谢Jk1提供了另一种建议。

答案 3 :(得分:0)

我意识到答案已被接受,但不幸的是它依赖于特定于Jetty的解决方案。我还没有尝试任何我想要提出的建议,但首先想到的是使用Endpoint.setExecutor(Executor)

一种可能性是创建一个ThreadFactory,为其创建的每个线程显式设置未捕获的异常处理程序。 Guava's ThreadFactoryBuilder可以提供帮助:

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");

另一种可能性是扩展ThreadPoolExecutor并覆盖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");