我编写了一个小型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);
答案 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");