package org.eclipse.jetty.http.spi; //======================================================================== //Copyright (c) 2004-2009 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. //======================================================================== import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpHandler; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import java.io.IOException; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * 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 Connector[] connectors = _server.getConnectors(); if (connectors != null) { for (Connector 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()); SelectChannelConnector connector = new SelectChannelConnector(); connector.setAcceptors(1); 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"); if (!(executor instanceof ThreadPoolExecutor)) throw new IllegalArgumentException("only java.util.concurrent.ThreadPoolExecutor instances are allowed, got: " + executor.getClass().getName()); if (LOG.isDebugEnabled()) LOG.debug("using ThreadPoolExecutor for server thread pool"); this._executor = (ThreadPoolExecutor) executor; _server.setThreadPool(new ThreadPoolExecutorAdapter(_executor)); } @Override public Executor getExecutor() { return _executor; } @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()); } }