简体   繁体   English

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

[英]How to gracefully close an Endpoint when an exception occurs?

I've written a small JAX-WS webservice that I'm running outside a container with Endpoint.publish() : 我编写了一个小型JAX-WS Web服务,我使用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. 如果我的任何Web服务方法抛出异常,则端点不会正常关闭,并且该地址将保持使用状态,直到Windows最终释放它为止。 This causes the classic error: 这会导致经典错误:

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

I could wrap all of my web service methods with try/catch, but this seems a little repetitive. 我可以用try / catch包装所有的web服务方法,但这似乎有点重复。 I also tried installing a clean-up class via Thread.setDefaultUncaughtExceptionHandler() , but this wasn't triggered when my web service method threw an exception. 我还尝试通过Thread.setDefaultUncaughtExceptionHandler()安装一个清理类,但是当我的Web服务方法引发异常时,没有触发此类。

Is there a more elegant way of solving this than resorting to countless try/catch blocks? 是否有一种更优雅的解决方法,而不是诉诸无数的try / catch块?


Based on Waldheinz's answer, I've attempted to use Jetty classes in favour of JDK defaults. 根据Waldheinz的回答,我试图使用Jetty类来支持JDK默认值。 The code compiles, but when executed it terminates immediately after publish . 代码编译,但在执行时,它会在publish后立即终止。 When using JDK classes, the main thread would remain alive until I manually terminated the process. 使用JDK类时,主线程将保持活动状态,直到我手动终止该进程。 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);

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

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

Using the hint from Waldheinz, I adjusted my web service to use Jetty as follows: 使用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());

This seems to be handling the problems well. 这似乎很好地处理了问题。 Bounty points go to Waldheinz for starting me down the right route. 恩惠积分去Waldheinz开始我正确的路线。 Thanks to Jk1 for an alternative suggestion. 感谢Jk1的替代建议。

I realize an answer's already been accepted, but it's unfortunate that it relies on a Jetty-specific solution. 我意识到答案已被接受,但不幸的是它依赖于Jetty特定的解决方案。 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) . 我没有尝试过任何我想提出的建议,但首先想到的是使用Endpoint.setExecutor(Executor)

One possibility is to create a ThreadFactory that explicitly sets the uncaught exception handler for every thread it creates. 一种可能性是创建一个ThreadFactory ,它为它创建的每个线程显式设置未捕获的异常处理程序。 Guava's ThreadFactoryBuilder can help: Guava的 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");

Another possibility is to extend ThreadPoolExecutor and override afterExecute(Runnable, Throwable) . 另一种可能性是扩展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");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM