| // |
| // ======================================================================== |
| // Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.http.spi; |
| |
| import java.io.IOException; |
| import java.net.InetSocketAddress; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.ThreadPoolExecutor; |
| |
| import org.eclipse.jetty.server.Connector; |
| import org.eclipse.jetty.server.Handler; |
| import org.eclipse.jetty.server.NetworkConnector; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.server.ServerConnector; |
| import org.eclipse.jetty.server.handler.ContextHandler; |
| import org.eclipse.jetty.server.handler.ContextHandlerCollection; |
| import org.eclipse.jetty.server.handler.HandlerCollection; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.eclipse.jetty.util.thread.ThreadPool; |
| |
| import com.sun.net.httpserver.HttpContext; |
| import com.sun.net.httpserver.HttpHandler; |
| |
| /** |
| * Jetty implementation of {@link com.sun.net.httpserver.HttpServer}. |
| */ |
| public class JettyHttpServer extends com.sun.net.httpserver.HttpServer |
| { |
| private static final Logger LOG = Log.getLogger(JettyHttpServer.class); |
| |
| |
| private Server _server; |
| |
| private boolean _serverShared; |
| |
| private InetSocketAddress _addr; |
| |
| private ThreadPoolExecutor _executor; |
| |
| private Map<String, JettyHttpContext> _contexts = new HashMap<String, JettyHttpContext>(); |
| |
| private Map<String, Connector> _connectors = new HashMap<String, Connector>(); |
| |
| |
| public JettyHttpServer(Server server, boolean shared) |
| { |
| this._server = server; |
| this._serverShared = shared; |
| } |
| |
| @Override |
| public void bind(InetSocketAddress addr, int backlog) throws IOException |
| { |
| // check if there is already a connector listening |
| Collection<NetworkConnector> connectors = _server.getBeans(NetworkConnector.class); |
| if (connectors != null) |
| { |
| for (NetworkConnector connector : connectors) |
| { |
| if (connector.getPort() == addr.getPort()) { |
| if (LOG.isDebugEnabled()) LOG.debug("server already bound to port " + addr.getPort() + ", no need to rebind"); |
| return; |
| } |
| } |
| } |
| |
| if (_serverShared) |
| throw new IOException("jetty server is not bound to port " + addr.getPort()); |
| |
| this._addr = addr; |
| |
| if (LOG.isDebugEnabled()) LOG.debug("binding server to port " + addr.getPort()); |
| ServerConnector connector = new ServerConnector(_server); |
| connector.setPort(addr.getPort()); |
| connector.setHost(addr.getHostName()); |
| _server.addConnector(connector); |
| |
| _connectors.put(addr.getHostName() + addr.getPort(), connector); |
| } |
| |
| @Override |
| public InetSocketAddress getAddress() |
| { |
| return _addr; |
| } |
| |
| @Override |
| public void start() |
| { |
| if (_serverShared) return; |
| |
| try |
| { |
| _server.start(); |
| } |
| catch (Exception ex) |
| { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| @Override |
| public void setExecutor(Executor executor) |
| { |
| if (executor == null) |
| throw new IllegalArgumentException("missing required 'executor' argument"); |
| ThreadPool threadPool = _server.getThreadPool(); |
| if (threadPool instanceof DelegatingThreadPool) |
| ((DelegatingThreadPool)_server.getThreadPool()).setExecutor(executor); |
| else |
| throw new UnsupportedOperationException("!DelegatingThreadPool"); |
| } |
| |
| @Override |
| public Executor getExecutor() |
| { |
| ThreadPool threadPool = _server.getThreadPool(); |
| if (threadPool instanceof DelegatingThreadPool) |
| return ((DelegatingThreadPool)_server.getThreadPool()).getExecutor(); |
| return threadPool; |
| } |
| |
| @Override |
| public void stop(int delay) |
| { |
| cleanUpContexts(); |
| cleanUpConnectors(); |
| |
| if (_serverShared) return; |
| |
| try |
| { |
| _server.stop(); |
| } |
| catch (Exception ex) |
| { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private void cleanUpContexts() |
| { |
| for (Map.Entry<String, JettyHttpContext> stringJettyHttpContextEntry : _contexts.entrySet()) |
| { |
| JettyHttpContext context = stringJettyHttpContextEntry.getValue(); |
| _server.removeBean(context.getJettyContextHandler()); |
| } |
| _contexts.clear(); |
| } |
| |
| private void cleanUpConnectors() |
| { |
| for (Map.Entry<String, Connector> stringConnectorEntry : _connectors.entrySet()) |
| { |
| Connector connector = stringConnectorEntry.getValue(); |
| try |
| { |
| connector.stop(); |
| } catch (Exception ex) { |
| LOG.warn(ex); |
| } |
| _server.removeConnector(connector); |
| } |
| _connectors.clear(); |
| } |
| |
| @Override |
| public HttpContext createContext(String path, HttpHandler httpHandler) |
| { |
| checkIfContextIsFree(path); |
| |
| JettyHttpContext context = new JettyHttpContext(this, path, httpHandler); |
| HttpSpiContextHandler jettyContextHandler = context.getJettyContextHandler(); |
| |
| ContextHandlerCollection chc = findContextHandlerCollection(_server.getHandlers()); |
| if (chc == null) |
| throw new RuntimeException("could not find ContextHandlerCollection, you must configure one"); |
| |
| chc.addHandler(jettyContextHandler); |
| _contexts.put(path, context); |
| |
| return context; |
| } |
| |
| private ContextHandlerCollection findContextHandlerCollection(Handler[] handlers) |
| { |
| if (handlers == null) |
| return null; |
| |
| for (Handler handler : handlers) |
| { |
| if (handler instanceof ContextHandlerCollection) |
| { |
| return (ContextHandlerCollection) handler; |
| } |
| |
| if (handler instanceof HandlerCollection) |
| { |
| HandlerCollection hc = (HandlerCollection) handler; |
| ContextHandlerCollection chc = findContextHandlerCollection(hc.getHandlers()); |
| if (chc != null) |
| return chc; |
| } |
| } |
| return null; |
| } |
| |
| private void checkIfContextIsFree(String path) |
| { |
| Handler serverHandler = _server.getHandler(); |
| if (serverHandler instanceof ContextHandler) |
| { |
| ContextHandler ctx = (ContextHandler) serverHandler; |
| if (ctx.getContextPath().equals(path)) |
| throw new RuntimeException("another context already bound to path " + path); |
| } |
| |
| Handler[] handlers = _server.getHandlers(); |
| if (handlers == null) return; |
| |
| for (Handler handler : handlers) |
| { |
| if (handler instanceof ContextHandler) { |
| ContextHandler ctx = (ContextHandler) handler; |
| if (ctx.getContextPath().equals(path)) |
| throw new RuntimeException("another context already bound to path " + path); |
| } |
| } |
| } |
| |
| @Override |
| public HttpContext createContext(String path) |
| { |
| return createContext(path, null); |
| } |
| |
| @Override |
| public void removeContext(String path) throws IllegalArgumentException |
| { |
| JettyHttpContext context = _contexts.remove(path); |
| if (context == null) return; |
| _server.removeBean(context.getJettyContextHandler()); |
| } |
| |
| @Override |
| public void removeContext(HttpContext context) |
| { |
| removeContext(context.getPath()); |
| } |
| |
| } |