Hi all, I'm at the HackerGarten @ JavaOne15, and write a patch for OpenJDK community. This's second times from JavaOne14. :)
We find an unexpected exception in JAX-WS, so I write a patch to fix it. We think that this issue may block the migration to JDK9 from JDK7. If we bind 0.0.0.0 ( using as wildcard ) to publish multiple as the following test code, JDK9 (and JDK8) returns "java.net.BindException: Address already in use.” as the below. But JDK7 does NOT return the exception. - Test code for reproduce --- import javax.jws.*; import javax.xml.ws.*; public class WSTest{ @WebService public static class Method1{ @WebMethod public String getMethod1Value(){ return "from Method1"; } } @WebService public static class Method2{ @WebMethod public String getMethod2Value(){ return "from Method2"; } } public static void main(String[] args) throws Exception{ Endpoint endPoint1 = Endpoint.publish("http://0.0.0.0:8081/method1", new Method1()); Endpoint endPoint2 = Endpoint.publish("http://0.0.0.0:8081/method2", new Method2()); System.out.println("Sleep 3 secs..."); Thread.sleep(3000); endPoint2.stop(); endPoint1.stop(); } } --- - StackTrace --- Exception in thread "main" com.sun.xml.internal.ws.server.ServerRtException: Server Runtime Error: java.net.BindException: Address already in use at com.sun.xml.internal.ws.transport.http.server.ServerMgr.createContext(ServerMgr.java:117) at com.sun.xml.internal.ws.transport.http.server.HttpEndpoint.publish(HttpEndpoint.java:64) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.publish(EndpointImpl.java:232) at com.sun.xml.internal.ws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:126) at javax.xml.ws.Endpoint.publish(Endpoint.java:240) at wstest.WSTest.main(WSTest.java:27) Caused by: java.net.BindException: Address already in use at sun.nio.ch.Net.bind0(Native Method) at sun.nio.ch.Net.bind(Net.java:432) at sun.nio.ch.Net.bind(Net.java:424) at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223) at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74) at sun.net.httpserver.ServerImpl.<init>(ServerImpl.java:102) at sun.net.httpserver.HttpServerImpl.<init>(HttpServerImpl.java:50) at sun.net.httpserver.DefaultHttpServerProvider.createHttpServer(DefaultHttpServerProvider.java:35) at com.sun.net.httpserver.HttpServer.create(HttpServer.java:130) at com.sun.xml.internal.ws.transport.http.server.ServerMgr.createContext(ServerMgr.java:86) ... 5 more ----- To publishes the Endpoint, JAX-WS checks whether the HttpContext has been created by given address, then creates a HttpContext if do not exist. If we sets 0.0.0.0 as given address, JAX-WS checks by ServerSocket#getLocalSocketAddress() (server local address), so returns BindException when 0.0.0.0 has been blinded already. Why so? JAX_WS-941[1] fixes NPE in Endpoint.stop but do not think about above situation. And JAX_WS-941 does not back port to JDK7. So I write a patch which is based jdk9/dev/jaxws (changeset: 637:2d84c6f4cbba) to fix the BindException with JAX_WS-941. Please review this patch :) [1]: https://java.net/jira/browse/JAX_WS-941 - Patch --- diff -r 2d84c6f4cbba src/java.xml.ws/share/classes/com/sun/xml/internal/ws/transport/http/server/ServerMgr.java --- a/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/transport/http/server/ServerMgr.java Thu Oct 22 08:47:47 2015 -0700 +++ b/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/transport/http/server/ServerMgr.java Tue Oct 27 19:48:35 2015 +0900 @@ -38,6 +38,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; +import java.util.Optional; /** * Manages all the WebService HTTP servers created by JAXWS runtime. @@ -81,24 +82,38 @@ synchronized(servers) { state = servers.get(inetAddress); if (state == null) { - logger.fine("Creating new HTTP Server at "+inetAddress); - // Creates server with default socket backlog - server = HttpServer.create(inetAddress, 0); - server.setExecutor(Executors.newCachedThreadPool()); - String path = url.toURI().getPath(); - logger.fine("Creating HTTP Context at = "+path); - HttpContext context = server.createContext(path); - server.start(); + final int finalPortNum = port; + Optional<ServerState> stateOpt = + servers.values() + .stream() + .filter(s -> s.getServer() + .getAddress() + .getPort() == finalPortNum) + .findAny(); - // we have to get actual inetAddress from server, which can differ from the original in some cases. - // e.g. A port number of zero will let the system pick up an ephemeral port in a bind operation, - // or IP: 0.0.0.0 - which is used to monitor network traffic from any valid IP address - inetAddress = server.getAddress(); + if (inetAddress.getAddress().isAnyLocalAddress() && + stateOpt.isPresent()) { + state = stateOpt.get(); + } else { + logger.fine("Creating new HTTP Server at "+inetAddress); + // Creates server with default socket backlog + server = HttpServer.create(inetAddress, 0); + server.setExecutor(Executors.newCachedThreadPool()); + String path = url.toURI().getPath(); + logger.fine("Creating HTTP Context at = "+path); + HttpContext context = server.createContext(path); + server.start(); - logger.fine("HTTP server started = "+inetAddress); - state = new ServerState(server, path); - servers.put(inetAddress, state); - return context; + // we have to get actual inetAddress from server, which can differ from the original in some cases. + // e.g. A port number of zero will let the system pick up an ephemeral port in a bind operation, + // or IP: 0.0.0.0 - which is used to monitor network traffic from any valid IP address + inetAddress = server.getAddress(); + + logger.fine("HTTP server started = "+inetAddress); + state = new ServerState(server, path); + servers.put(inetAddress, state); + return context; + } } } server = state.getServer(); ---