All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ebay.jetstream.servlet.JettyServer Maven / Gradle / Ivy

/*******************************************************************************
 *  Copyright © 2012-2015 eBay Software Foundation
 *  This program is dual licensed under the MIT and Apache 2.0 licenses.
 *  Please see LICENSE for more information.
 *******************************************************************************/
/**
 *
 */
package com.ebay.jetstream.servlet;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.ByteChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.mortbay.io.Buffer;
import org.mortbay.io.nio.ChannelEndPoint;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.HttpSchemes;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.nio.AbstractNIOConnector;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.thread.BoundedThreadPool;
import org.mortbay.thread.QueuedThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.context.event.ContextStoppedEvent;

import com.ebay.jetstream.common.ShutDownable;
import com.ebay.jetstream.config.ConfigUtils;
import com.ebay.jetstream.servlet.SSLServerHost;
import com.ebay.jetstream.servlet.ServletDefinition;

@SuppressWarnings("deprecation")
public class JettyServer implements ApplicationListener, InitializingBean, ShutDownable {

  static class SimpleNIOConnector extends AbstractNIOConnector {
    private transient ServerSocketChannel m_acceptChannel;
    private final InetAddress m_bindAddress;

    public SimpleNIOConnector(InetAddress address, int port) {
      m_bindAddress = address;
      setHost(m_bindAddress.getHostName());

      setPort(port);
      setUseDirectBuffers(false);
    }

    @Override
    protected void accept(int acceptorID) throws IOException, InterruptedException {
      SocketChannel channel = m_acceptChannel.accept();
      channel.configureBlocking(true);
      Socket socket = channel.socket();
      configure(socket);
      SimpleNIOHttpEndPoint endpoint = new SimpleNIOHttpEndPoint(this, channel);
      endpoint.dispatch();
    }

    public void close() throws IOException {
      if (m_acceptChannel != null) {
        m_acceptChannel.close();
      }
      m_acceptChannel = null;
    }

    @Override
    protected void connectionClosed(HttpConnection connection) {
      super.connectionClosed(connection);
    }

    @Override
    protected void connectionOpened(HttpConnection connection) {
      super.connectionOpened(connection);
    }

    public Object getConnection() {
      return m_acceptChannel;
    }

    public int getLocalPort() {
      return m_acceptChannel == null || !m_acceptChannel.isOpen() ? -1 : m_acceptChannel.socket().getLocalPort();
    }

    public void open() throws IOException {
      // Create a new server socket and set to blocking mode as for now
      m_acceptChannel = ServerSocketChannel.open();
      m_acceptChannel.configureBlocking(true);
      // Bind the server socket to the address and port
      InetSocketAddress addr = new InetSocketAddress(m_bindAddress, getPort());
      setAcceptQueueSize(100);
      setIntegralScheme(HttpSchemes.HTTP);
      setConfidentialScheme(HttpSchemes.HTTP);
      m_acceptChannel.socket().bind(addr, getAcceptQueueSize());
    }
  }

  static class SimpleNIOHttpEndPoint extends ChannelEndPoint implements Runnable {
    private int m_sotimeout;
    private final HttpConnection m_connection;
    private final SimpleNIOConnector m_connector;

    public SimpleNIOHttpEndPoint(SimpleNIOConnector connector, ByteChannel channel) {
      super(channel);
      m_connector = connector;
      m_connection = new HttpConnection(connector, this, connector.getServer());
    }

    protected void connectionClosed() {
      m_connector.connectionClosed(m_connection);
    }

    protected void connectionOpened() {
      m_connector.connectionOpened(m_connection);
    }

    public void dispatch() throws IOException {
      if (!m_connector.getThreadPool().dispatch(this)) {
        LOGGER.warn( "dispatch failed for " + m_connection.toString());
        close();
      }
    }

    @Override
    public int fill(Buffer buffer) throws IOException {
      int len = super.fill(buffer);
      if (len < 0)
        getChannel().close();
      return len;
    }

    public void run() {
      try {
        connectionOpened();
        while (isOpen()) {
          if (m_connection.isIdle()) {
            if (m_connector.getServer().getThreadPool().isLowOnThreads()) {
              if (m_sotimeout != m_connector.getLowResourceMaxIdleTime()) {
                m_sotimeout = m_connector.getLowResourceMaxIdleTime();
                ((SocketChannel) getChannel()).socket().setSoTimeout(m_sotimeout);
              }
            }
          }
          // Processing Http Request
          m_connection.handle();
        }
      }
      catch (Exception e) {
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug( "Failed", e);
        }
        try {
          close();
        }
        catch (IOException e1) {
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug( "closing", e1);
          }
        }
      }
      finally {
        connectionClosed();
      }
    }

  }

  private final String LOGGING_COMPONENT = this.getClass().getSimpleName();

  /**
   * Wanted to name it SSLHost but the spring init order was loading this after the servlet def (which infact required
   * this host to be loaded early on)
   */
  private SSLServerHost m_httpsHost;

  private static final Logger LOGGER = LoggerFactory.getLogger(JettyServer.class.getName());

  private boolean m_lazy = true;

  private boolean m_immediateStart = false;

  private InetAddress m_address;

  private int m_port;

  private int m_minThreadPoolSize = 3;

  private int m_maxThreadPoolSize = 9;

  private boolean m_boundedThreadPool = true;

  private String m_contextPath = "/";

  private String m_resourceBase;
  private final List m_servletDefinitions = new ArrayList();
  private Server m_server;
  private ContextHandlerCollection m_contexts;
  private Context m_context;
  private String m_webAppcontextPath;

  public String getWebAppcontextPath() {
	return m_webAppcontextPath;
}

public void setWebAppcontextPath(String m_webAppcontextPath) {
	this.m_webAppcontextPath = m_webAppcontextPath;
}

public void afterPropertiesSet() throws Exception {
    initServletDefinitions();
    if (isImmediateStart()){
      start();
      m_context.start();
    } 
  }

  public InetAddress getAddress() {
    return m_address;
  }
 
	protected Context getContext() {
		if (getWebAppcontextPath() != null) {
			m_context = new WebAppContext(
					null,ConfigUtils
					.getInitialPropertyExpanded(getContextPath()));
			m_context.setResourceBase(getResourceBase());
			getContexts().addHandler(m_context);
			getServer().setHandler(getContexts());
		} else if (m_context == null) {
			getServer().setHandler(getContexts());
			m_context = new Context(m_contexts, getContextPath(),
					Context.SESSIONS);
			if (getResourceBase() != null) {
				m_context.setResourceBase(getResourceBase());
			}
		}
		return m_context;
	}

  public String getContextPath() {
    return m_contextPath;
  }

  protected ContextHandlerCollection getContexts() {
    if (m_contexts == null)
      m_contexts = new ContextHandlerCollection();

    return m_contexts;
  }

  public int getPendingEvents() {
    // TODO Auto-generated method stub
    return 0;
  }

  public SSLServerHost getHttpsHost() {
    return m_httpsHost;
  }

  public int getMaxThreadPoolSize() {
    return m_maxThreadPoolSize;
  }

  public int getMinThreadPoolSize() {
    return m_minThreadPoolSize;
  }

  public int getPort() {
    return m_port;
  }

  public String getResourceBase() {
    return m_resourceBase;
  }

 
  public Server getServer() {
    if (m_server == null) {

      /*
       * try { m_address = InetAddress.getLocalHost(); // temporary to testSimpleNIOConnector } catch
       * (UnknownHostException e) {
       * 
       * e.printStackTrace(); }
       */

      InetAddress addr = getAddress();

      int port = getPort();

      if (addr == null)
        m_server = new Server(port);
      else
        m_server = new Server();

      if (getHttpsHost() != null)
        m_server.setConnectors(new Connector[] { getSSLConnector() });
      else if (addr != null) {
    	  LOGGER.warn( "SSL Host NOT Configured. Using Http");
        m_server.setConnectors(new Connector[] { new SimpleNIOConnector(addr, port) });
      }

      if (isBoundedThreadPool()) {
        BoundedThreadPool threadPool = new BoundedThreadPool();
        threadPool.setMinThreads(getMinThreadPoolSize());
        threadPool.setMaxThreads(getMaxThreadPoolSize());
        m_server.setThreadPool(threadPool);
      }
      else {
        QueuedThreadPool threadPool = new QueuedThreadPool();
        threadPool.setMinThreads(getMinThreadPoolSize());
        threadPool.setMaxThreads(getMaxThreadPoolSize());
        threadPool.setSpawnOrShrinkAt(getMinThreadPoolSize());
        m_server.setThreadPool(threadPool);
      }

    }
    return m_server;
  }

  /**
   * @return the servletDefinitions
   */
  public List getServletDefinitions() {
    return m_servletDefinitions;
  }

  private Connector getSSLConnector() {
    SslSocketConnector sslConnector = new SslSocketConnector();
    sslConnector.setPort(getPort());
    sslConnector.setKeyPassword(getHttpsHost().getKeyStorePassword());
    sslConnector.setKeystore(getHttpsHost().getKeyStorePath());
    sslConnector.setTruststore(getHttpsHost().getTrustStorePath());
    sslConnector.setTrustPassword(getHttpsHost().getTrustStorePassword());
    return sslConnector;
  }

  /**
   * @param servletDefinitions
   *          the servletDefinitions to set
   * @throws ClassNotFoundException
   */
  private void initServletDefinitions() {
    try {
      Context context = getContext();
      int order = isLazy() ? -1 : 0;
      for (ServletDefinition sd : m_servletDefinitions) {
        ServletHolder holder = new ServletHolder(sd.getServletClass());
        if (order >= 0)
          order++;
        holder.setInitOrder(order);
        Map initParams = sd.getInitParams();
        if (initParams != null)
          holder.setInitParameters(initParams);
        context.addServlet(holder, sd.getUrlPath());
        
      }
    }
    catch (Exception e) {
    	LOGGER.error( e.getMessage(), e);
    }

  }

  public boolean isBoundedThreadPool() {
    return m_boundedThreadPool;
  }

  public boolean isImmediateStart() {
    return m_immediateStart;
  }

  public boolean isLazy() {
    return m_lazy;
  }

  public void join() {
    try {
      getServer().join();
    }
    catch (InterruptedException e) {
      LOGGER.warn( e.getMessage());
    }
  }

  public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ContextStartedEvent && !isImmediateStart()) {
      try {
        start();
      }
      catch (Exception e) {
        LOGGER.error( e.getLocalizedMessage(),  e);
      }
    }
    else if (event instanceof ContextClosedEvent || event instanceof ContextStoppedEvent) {
      stop();
    }
  }

  public void setAddress(InetAddress address) {
    m_address = address;
  }

  public void setBoundedThreadPool(boolean boundedThreadPool) {
    m_boundedThreadPool = boundedThreadPool;
  }

  public void setContextPath(String contextPath) {
    m_contextPath = contextPath;
  }

  public void setHttpsHost(SSLServerHost httpsHost) {
    m_httpsHost = httpsHost;
  }

  public void setImmediateStart(boolean earlyStart) {
    m_immediateStart = earlyStart;
  }

  public void setLazy(boolean lazy) {
    m_lazy = lazy;
  }

  public void setMaxThreadPoolSize(int maxThreadPoolSize) {
    m_maxThreadPoolSize = maxThreadPoolSize;
  }

  public void setMinThreadPoolSize(int minThreadPoolSize) {
    m_minThreadPoolSize = minThreadPoolSize;
  }

  public void setPort(int httpListenerPort) {
    m_port = httpListenerPort;
  }

  public void setResourceBase(String resourceBase) {
    m_resourceBase = ConfigUtils.getInitialPropertyExpanded(resourceBase);
  }

  public void setServletDefinitions(List servletDefinitions) {
    m_servletDefinitions.clear();
    m_servletDefinitions.addAll(servletDefinitions);
  }

  public void shutDown() {
    stop();
  }

  public void start() throws Exception {
    try {
      LOGGER.warn( "Starting Jetty Server.");
      getServer().start();
    }
    catch (Exception e) {
      LOGGER.error( "Failed to start Jetty: " + e);
      throw e;
    }
  }

  public void stop() {
    try {
      getServer().stop();
    }
    catch (Exception e) {
      LOGGER.error( "Failed to stop Jetty: " + e);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy