1 /*
   2  * Copyright (c) 1997, 2015, 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 
  26 package com.sun.xml.internal.ws.transport.http.server;
  27 
  28 import com.sun.net.httpserver.HttpContext;
  29 import com.sun.net.httpserver.HttpServer;
  30 import com.sun.xml.internal.ws.server.ServerRtException;
  31 
  32 import java.net.InetSocketAddress;
  33 import java.net.URL;
  34 import java.util.HashMap;
  35 import java.util.HashSet;
  36 import java.util.Map;
  37 import java.util.Set;
  38 import java.util.concurrent.ExecutorService;
  39 import java.util.concurrent.Executors;
  40 import java.util.logging.Logger;
  41 import java.util.Optional;
  42 
  43 
  44 /**
  45  * Manages all the WebService HTTP servers created by JAXWS runtime.
  46  *
  47  * @author Jitendra Kotamraju
  48  */
  49 final 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<InetSocketAddress,ServerState>();
  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                     final int finalPortNum = port;
  87                     Optional<ServerState> stateOpt =
  88                             servers.values()
  89                                     .stream()
  90                                     .filter(s -> s.getServer()
  91                                             .getAddress()
  92                                             .getPort() == finalPortNum)
  93                                     .findAny();
  94 
  95                     if (inetAddress.getAddress().isAnyLocalAddress() &&
  96                             stateOpt.isPresent()) {
  97                         state = stateOpt.get();
  98                     } else {
  99                         logger.fine("Creating new HTTP Server at "+inetAddress);
 100                         // Creates server with default socket backlog
 101                         server = HttpServer.create(inetAddress, 0);
 102                         server.setExecutor(Executors.newCachedThreadPool());
 103                         String path = url.toURI().getPath();
 104                         logger.fine("Creating HTTP Context at = "+path);
 105                         HttpContext context = server.createContext(path);
 106                         server.start();
 107 
 108                         // we have to get actual inetAddress from server, which can differ from the original in some cases.
 109                         // e.g. A port number of zero will let the system pick up an ephemeral port in a bind operation,
 110                         // or IP: 0.0.0.0 - which is used to monitor network traffic from any valid IP address
 111                         inetAddress = server.getAddress();
 112 
 113                         logger.fine("HTTP server started = "+inetAddress);
 114                         state = new ServerState(server, path);
 115                         servers.put(inetAddress, state);
 116                         return context;
 117                     }
 118                 }
 119             }
 120             server = state.getServer();
 121 
 122             if (state.getPaths().contains(url.getPath())) {
 123                 String err = "Context with URL path "+url.getPath()+ " already exists on the server "+server.getAddress();
 124                 logger.fine(err);
 125                 throw new IllegalArgumentException(err);
 126             }
 127 
 128             logger.fine("Creating HTTP Context at = "+url.getPath());
 129             HttpContext context = server.createContext(url.getPath());
 130             state.oneMoreContext(url.getPath());
 131             return context;
 132         } catch(Exception e) {
 133             throw new ServerRtException("server.rt.err",e );
 134         }
 135     }
 136 
 137     /*
 138      * Removes a context. If the server doesn't have anymore contexts, it
 139      * would stop the server and server is removed from servers Map.
 140      */
 141     /*package*/ void removeContext(HttpContext context) {
 142         InetSocketAddress inetAddress = context.getServer().getAddress();
 143         synchronized(servers) {
 144             ServerState state = servers.get(inetAddress);
 145             int instances = state.noOfContexts();
 146             if (instances < 2) {
 147                 ((ExecutorService)state.getServer().getExecutor()).shutdown();
 148                 state.getServer().stop(0);
 149                 servers.remove(inetAddress);
 150             } else {
 151                 state.getServer().removeContext(context);
 152                 state.oneLessContext(context.getPath());
 153             }
 154         }
 155     }
 156 
 157     private static final class ServerState {
 158         private final HttpServer server;
 159         private int instances;
 160         private Set<String> paths = new HashSet<String>();
 161 
 162         ServerState(HttpServer server, String path) {
 163             this.server = server;
 164             this.instances = 1;
 165             paths.add(path);
 166         }
 167 
 168         public HttpServer getServer() {
 169             return server;
 170         }
 171 
 172         public void oneMoreContext(String path) {
 173             ++instances;
 174             paths.add(path);
 175         }
 176 
 177         public void oneLessContext(String path) {
 178             --instances;
 179             paths.remove(path);
 180         }
 181 
 182         public int noOfContexts() {
 183             return instances;
 184         }
 185 
 186         public Set<String> getPaths() {
 187             return paths;
 188         }
 189     }
 190 }