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

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

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

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

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

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

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


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

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

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

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

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

使用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提供了另一种建议。

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

一种可能性是创建一个ThreadFactory ,它为它创建的每个线程显式设置未捕获的exception处理程序。 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"); 

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