1/* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.xml.internal.ws.transport.http.server; 27 28import com.sun.net.httpserver.HttpContext; 29import com.sun.net.httpserver.HttpServer; 30import com.sun.xml.internal.ws.server.ServerRtException; 31 32import java.net.InetSocketAddress; 33import java.net.URL; 34import java.util.HashMap; 35import java.util.HashSet; 36import java.util.Map; 37import java.util.Set; 38import java.util.concurrent.ExecutorService; 39import java.util.concurrent.Executors; 40import java.util.logging.Level; 41import java.util.logging.Logger; 42 43 44/** 45 * Manages all the WebService HTTP servers created by JAXWS runtime. 46 * 47 * @author Jitendra Kotamraju 48 */ 49final class ServerMgr { 50 51 private static final ServerMgr serverMgr = new ServerMgr(); 52 private static final Logger LOGGER = 53 Logger.getLogger( 54 com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.http"); 55 private final Map<InetSocketAddress,ServerState> servers = new HashMap<>(); 56 57 private ServerMgr() {} 58 59 /** 60 * Gets the singleton instance. 61 * @return manager instance 62 */ 63 static ServerMgr getInstance() { 64 return serverMgr; 65 } 66 67 /* 68 * Creates a HttpContext at the given address. If there is already a server 69 * it uses that server to create a context. Otherwise, it creates a new 70 * HTTP server. This sever is added to servers Map. 71 */ 72 /*package*/ HttpContext createContext(String address) { 73 try { 74 HttpServer server; 75 ServerState state; 76 URL url = new URL(address); 77 int port = url.getPort(); 78 if (port == -1) { 79 port = url.getDefaultPort(); 80 } 81 InetSocketAddress inetAddress = new InetSocketAddress(url.getHost(), 82 port); 83 synchronized(servers) { 84 state = servers.get(inetAddress); 85 if (state == null) { 86 ServerState free = null; 87 for (ServerState ss : servers.values()) { 88 if (port == ss.getServer().getAddress().getPort()) { 89 free = ss; 90 break; 91 } 92 } 93 if (inetAddress.getAddress().isAnyLocalAddress() && free != null) { 94 state = free; 95 } else { 96 if (LOGGER.isLoggable(Level.FINE)) { 97 LOGGER.fine("Creating new HTTP Server at "+inetAddress); 98 } 99 // Creates server with default socket backlog 100 server = HttpServer.create(inetAddress, 0); 101 server.setExecutor(Executors.newCachedThreadPool()); 102 String path = url.toURI().getPath(); 103 if (LOGGER.isLoggable(Level.FINE)) { 104 LOGGER.fine("Creating HTTP Context at = "+path); 105 } 106 HttpContext context = server.createContext(path); 107 server.start(); 108 109 // we have to get actual inetAddress from server, which can differ from the original in some cases. 110 // e.g. A port number of zero will let the system pick up an ephemeral port in a bind operation, 111 // or IP: 0.0.0.0 - which is used to monitor network traffic from any valid IP address 112 inetAddress = server.getAddress(); 113 114 if (LOGGER.isLoggable(Level.FINE)) { 115 LOGGER.fine("HTTP server started = "+inetAddress); 116 } 117 state = new ServerState(server, path); 118 servers.put(inetAddress, state); 119 return context; 120 } 121 } 122 } 123 server = state.getServer(); 124 125 if (state.getPaths().contains(url.getPath())) { 126 String err = "Context with URL path "+url.getPath()+ " already exists on the server "+server.getAddress(); 127 if (LOGGER.isLoggable(Level.FINE)) { 128 LOGGER.fine(err); 129 } 130 throw new IllegalArgumentException(err); 131 } 132 133 if (LOGGER.isLoggable(Level.FINE)) { 134 LOGGER.fine("Creating HTTP Context at = "+url.getPath()); 135 } 136 HttpContext context = server.createContext(url.getPath()); 137 state.oneMoreContext(url.getPath()); 138 return context; 139 } catch(Exception e) { 140 throw new ServerRtException("server.rt.err",e ); 141 } 142 } 143 144 /* 145 * Removes a context. If the server doesn't have anymore contexts, it 146 * would stop the server and server is removed from servers Map. 147 */ 148 /*package*/ void removeContext(HttpContext context) { 149 InetSocketAddress inetAddress = context.getServer().getAddress(); 150 synchronized(servers) { 151 ServerState state = servers.get(inetAddress); 152 int instances = state.noOfContexts(); 153 if (instances < 2) { 154 ((ExecutorService)state.getServer().getExecutor()).shutdown(); 155 state.getServer().stop(0); 156 servers.remove(inetAddress); 157 } else { 158 state.getServer().removeContext(context); 159 state.oneLessContext(context.getPath()); 160 } 161 } 162 } 163 164 private static final class ServerState { 165 private final HttpServer server; 166 private int instances; 167 private final Set<String> paths = new HashSet<>(); 168 169 ServerState(HttpServer server, String path) { 170 this.server = server; 171 this.instances = 1; 172 paths.add(path); 173 } 174 175 public HttpServer getServer() { 176 return server; 177 } 178 179 public void oneMoreContext(String path) { 180 ++instances; 181 paths.add(path); 182 } 183 184 public void oneLessContext(String path) { 185 --instances; 186 paths.remove(path); 187 } 188 189 public int noOfContexts() { 190 return instances; 191 } 192 193 public Set<String> getPaths() { 194 return paths; 195 } 196 } 197} 198