com.betfair.cougar.transport.jetty.JettyServerWrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.betfair.cougar.transport.jetty.JettyServerWrapper.java

Source

/*
 * Copyright 2014, The Sporting Exchange Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.betfair.cougar.transport.jetty;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.betfair.cougar.util.KeyStoreManagement;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ConnectorStatistics;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.LowResourceMonitor;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.springframework.core.io.Resource;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

/**
 * This class encapsulate configuration and lifecycle of a Jetty server and its connectors
 */
public class JettyServerWrapper {
    private static final Logger LOGGER = LoggerFactory.getLogger(JettyServerWrapper.class);

    private Server jettyServer;

    private MBeanServer mbeanServer;

    private QueuedThreadPool threadPool;

    private int httpPort;
    private boolean httpReuseAddress;
    private int httpMaxIdle;
    private int httpAcceptors;
    private int httpAcceptQueueSize;
    private int httpSelectors;

    private boolean httpsAllowRenegotiate;

    private boolean httpForwarded;
    private boolean httpsForwarded;

    private int requestHeaderSize;
    private int responseHeaderSize;
    private int responseBufferSize;

    private int httpsPort;
    private boolean httpsReuseAddress;
    private int httpsMaxIdle;
    private int httpsAcceptors;
    private int httpsAcceptQueueSize;
    private int httpsSelectors;

    private boolean httpsWantClientAuth;
    private boolean httpsNeedClientAuth;
    private Resource httpsKeystore;
    private String httpsKeystoreType;
    private String httpsKeyPassword;
    private String httpsCertAlias;
    private Resource httpsTruststore;
    private String httpsTruststoreType;
    private String httpsTrustPassword;

    private int minThreads;
    private int maxThreads;

    private ServerConnector httpConnector;
    private HttpConfiguration httpConfiguration;
    private ServerConnector httpsConnector;
    private HttpConfiguration httpsConfiguration;

    private int maxFormContentSize;

    private int lowResourcesIdleTime;
    private int lowResourcesMaxTime;
    private int lowResourcesPeriod;
    private boolean lowResourcesMonitorThreads;
    private int lowResourcesMaxConnections;
    private long lowResourcesMaxMemory;

    public void initialiseConnectors() throws Exception {
        threadPool = new QueuedThreadPool();
        threadPool.setMaxThreads(maxThreads);
        threadPool.setMinThreads(minThreads);
        threadPool.setName("JettyThread");
        jettyServer = new Server(threadPool);

        jettyServer.setStopAtShutdown(true);

        MBeanContainer container = new MBeanContainer(mbeanServer);
        jettyServer.addBean(container);

        LowResourceMonitor lowResourcesMonitor = new LowResourceMonitor(jettyServer);
        lowResourcesMonitor.setPeriod(lowResourcesPeriod);
        lowResourcesMonitor.setLowResourcesIdleTimeout(lowResourcesIdleTime);
        lowResourcesMonitor.setMonitorThreads(lowResourcesMonitorThreads);
        lowResourcesMonitor.setMaxConnections(lowResourcesMaxConnections);
        lowResourcesMonitor.setMaxMemory(lowResourcesMaxMemory);
        lowResourcesMonitor.setMaxLowResourcesTime(lowResourcesMaxTime);
        jettyServer.addBean(lowResourcesMonitor);

        // US24803 - Needed for preventing Hashtable key collision DoS CVE-2012-2739
        jettyServer.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize", maxFormContentSize);

        List<Connector> connectors = new ArrayList<Connector>();

        if (httpPort != -1) {
            httpConfiguration = createHttpConfiguration();
            setBufferSizes(httpConfiguration);
            if (httpForwarded) {
                httpConfiguration.addCustomizer(new ForwardedRequestCustomizer());
            }
            httpConnector = createHttpConnector(jettyServer, httpConfiguration, httpAcceptors, httpSelectors);
            httpConnector.setPort(httpPort);
            httpConnector.setReuseAddress(httpReuseAddress);
            httpConnector.setIdleTimeout(httpMaxIdle);
            httpConnector.setAcceptQueueSize(httpAcceptQueueSize);
            httpConnector.addBean(new ConnectorStatistics());

            connectors.add(httpConnector);
        }

        if (httpsPort != -1) {
            SslContextFactory sslContextFactory = new SslContextFactory();
            sslContextFactory.setKeyStorePath(httpsKeystore.getFile().getCanonicalPath());
            sslContextFactory.setKeyStoreType(httpsKeystoreType);
            sslContextFactory.setKeyStorePassword(httpsKeyPassword);
            if (StringUtils.isNotBlank(httpsCertAlias)) {
                sslContextFactory.setCertAlias(httpsCertAlias);
            }
            sslContextFactory.setKeyManagerPassword(httpsKeyPassword);
            // if you need it then you defo want it
            sslContextFactory.setWantClientAuth(httpsNeedClientAuth || httpsWantClientAuth);
            sslContextFactory.setNeedClientAuth(httpsNeedClientAuth);
            sslContextFactory.setRenegotiationAllowed(httpsAllowRenegotiate);

            httpsConfiguration = createHttpConfiguration();
            setBufferSizes(httpsConfiguration);
            if (httpsForwarded) {
                httpsConfiguration.addCustomizer(new ForwardedRequestCustomizer());
            }

            httpsConnector = createHttpsConnector(jettyServer, httpsConfiguration, httpsAcceptors, httpsSelectors,
                    sslContextFactory);
            httpsConnector.setPort(httpsPort);
            httpsConnector.setReuseAddress(httpsReuseAddress);
            httpsConnector.setIdleTimeout(httpsMaxIdle);
            httpsConnector.setAcceptQueueSize(httpsAcceptQueueSize);
            httpsConnector.addBean(new ConnectorStatistics());

            mbeanServer.registerMBean(getKeystoreCertificateChains(), new ObjectName("CoUGAR.https:name=keyStore"));
            // truststore is not required if we don't want client auth
            if (httpsWantClientAuth) {
                sslContextFactory.setTrustStorePath(httpsTruststore.getFile().getCanonicalPath());
                sslContextFactory.setTrustStoreType(httpsTruststoreType);
                sslContextFactory.setTrustStorePassword(httpsTrustPassword);
                mbeanServer.registerMBean(getTruststoreCertificateChains(),
                        new ObjectName("CoUGAR.https:name=trustStore"));
            }
            connectors.add(httpsConnector);
        }

        if (connectors.size() == 0) {
            throw new IllegalStateException(
                    "HTTP transport requires at least one port enabled to function correctly.");
        }

        jettyServer.setConnectors(connectors.toArray(new Connector[connectors.size()]));
    }

    public void start() throws Exception {
        jettyServer.start();
    }

    public void startThreadPool() throws Exception {
        threadPool.start();
    }

    public void stop() throws Exception {
        jettyServer.stop();
    }

    public boolean isRunning() {
        if (jettyServer == null) {
            return false;
        }
        return jettyServer.isRunning();
    }

    private HttpConfiguration createHttpConfiguration() {
        HttpConfiguration httpConfiguration = new HttpConfiguration();
        httpConfiguration.setSendServerVersion(true);
        httpConfiguration.setSendDateHeader(true);
        return httpConfiguration;
    }

    /**
     * Sets the request and header buffer sizex if they are not zero
     */
    private void setBufferSizes(HttpConfiguration buffers) {
        if (requestHeaderSize > 0) {
            LOGGER.info("Request header size set to {} for {}", requestHeaderSize,
                    buffers.getClass().getCanonicalName());
            buffers.setRequestHeaderSize(requestHeaderSize);
        }

        if (responseBufferSize > 0) {
            LOGGER.info("Response buffer size set to {} for {}", responseBufferSize,
                    buffers.getClass().getCanonicalName());
            buffers.setOutputBufferSize(responseBufferSize);
        }
        if (responseHeaderSize > 0) {
            LOGGER.info("Response header size set to {} for {}", responseHeaderSize,
                    buffers.getClass().getCanonicalName());
            buffers.setResponseHeaderSize(responseHeaderSize);
        }
    }

    protected ServerConnector createHttpConnector(Server server, HttpConfiguration httpConfiguration,
            int httpAcceptors, int httpSelectors) {
        return new ServerConnector(server, null, null, null, httpAcceptors, httpSelectors,
                new HttpConnectionFactory(httpConfiguration));
    }

    protected ServerConnector createHttpsConnector(Server server, HttpConfiguration httpConfiguration,
            int httpsAcceptors, int httpsSelectors, SslContextFactory sslContextFactory) {
        httpConfiguration.addCustomizer(new SecureRequestCustomizer());
        return new ServerConnector(server, null, null, null, httpsAcceptors, httpsSelectors,
                new SslConnectionFactory(sslContextFactory, "http/1.1"),
                new HttpConnectionFactory(httpConfiguration));
    }

    private KeyStoreManagement getKeystoreCertificateChains() throws Exception {
        return KeyStoreManagement.getKeyStoreManagement(httpsKeystoreType, httpsKeystore, httpsKeyPassword);
    }

    private KeyStoreManagement getTruststoreCertificateChains() throws Exception {
        return KeyStoreManagement.getKeyStoreManagement(httpsTruststoreType, httpsTruststore, httpsTrustPassword);
    }

    public boolean addBean(Object o) {
        return jettyServer.addBean(o);
    }

    public void setHandler(Handler handler) {
        jettyServer.setHandler(handler);
    }

    public boolean isHttpEnabled() {
        return httpPort != -1;
    }

    public boolean isHttpsEnabled() {
        return httpsPort != -1;
    }

    public int getRequestHeaderSize() {
        return requestHeaderSize;
    }

    public int getResponseBufferSize() {
        return responseBufferSize;
    }

    public int getResponseHeaderSize() {
        return responseHeaderSize;
    }

    public int getHttpAcceptors() {
        return httpAcceptors;
    }

    public int getHttpsAcceptors() {
        return httpsAcceptors;
    }

    public boolean isHttpForwarded() {
        return httpForwarded;
    }

    public void setMbeanServer(MBeanServer mbeanServer) {
        this.mbeanServer = mbeanServer;
    }

    public void setHttpPort(int httpPort) {
        this.httpPort = httpPort;
    }

    public int getHttpPort() {
        return httpPort;
    }

    public void setHttpReuseAddress(boolean httpReuseAddress) {
        this.httpReuseAddress = httpReuseAddress;
    }

    public void setHttpMaxIdle(int httpMaxIdle) {
        this.httpMaxIdle = httpMaxIdle;
    }

    public void setHttpAcceptors(int httpAcceptors) {
        this.httpAcceptors = httpAcceptors;
    }

    public void setHttpSelectors(int httpSelectors) {
        this.httpSelectors = httpSelectors;
    }

    public int getHttpSelectors() {
        return httpSelectors;
    }

    public void setHttpForwarded(boolean httpForwarded) {
        this.httpForwarded = httpForwarded;
    }

    public boolean isHttpsForwarded() {
        return httpsForwarded;
    }

    public void setRequestHeaderSize(int requestHeaderSize) {
        this.requestHeaderSize = requestHeaderSize;
    }

    public void setResponseHeaderSize(int responseHeaderSize) {
        this.responseHeaderSize = responseHeaderSize;
    }

    public void setResponseBufferSize(int responseBufferSize) {
        this.responseBufferSize = responseBufferSize;
    }

    public void setHttpsPort(int httpsPort) {
        this.httpsPort = httpsPort;
    }

    public int getHttpsPort() {
        return httpsPort;
    }

    public void setHttpsReuseAddress(boolean httpsReuseAddress) {
        this.httpsReuseAddress = httpsReuseAddress;
    }

    public void setHttpsMaxIdle(int httpsMaxIdle) {
        this.httpsMaxIdle = httpsMaxIdle;
    }

    public void setHttpsAcceptors(int httpsAcceptors) {
        this.httpsAcceptors = httpsAcceptors;
    }

    public void setHttpsSelectors(int httpsSelectors) {
        this.httpsSelectors = httpsSelectors;
    }

    public int getHttpsSelectors() {
        return httpsSelectors;
    }

    public void setHttpsWantClientAuth(boolean httpsWantClientAuth) {
        this.httpsWantClientAuth = httpsWantClientAuth;
    }

    public void setHttpsNeedClientAuth(boolean httpsNeedClientAuth) {
        this.httpsNeedClientAuth = httpsNeedClientAuth;
    }

    public void setHttpsKeystore(Resource httpsKeystore) {
        this.httpsKeystore = httpsKeystore;
    }

    public void setHttpsKeystoreType(String httpsKeystoreType) {
        this.httpsKeystoreType = httpsKeystoreType;
    }

    public void setHttpsKeyPassword(String httpsKeyPassword) {
        this.httpsKeyPassword = httpsKeyPassword;
    }

    public void setHttpsTruststore(Resource httpsTruststore) {
        this.httpsTruststore = httpsTruststore;
    }

    public void setHttpsTruststoreType(String httpsTruststoreType) {
        this.httpsTruststoreType = httpsTruststoreType;
    }

    public void setHttpsTrustPassword(String httpsTrustPassword) {
        this.httpsTrustPassword = httpsTrustPassword;
    }

    public void setMinThreads(int minThreads) {
        this.minThreads = minThreads;
    }

    public void setMaxThreads(int maxThreads) {
        this.maxThreads = maxThreads;
    }

    public boolean isHttpsAllowRenegotiate() {
        return httpsAllowRenegotiate;
    }

    public void setHttpsAllowRenegotiate(boolean httpsAllowRenegotiate) {
        this.httpsAllowRenegotiate = httpsAllowRenegotiate;
    }

    public Server getJettyServer() {
        return jettyServer;
    }

    /* Some testing helpers */
    public boolean isHttpConnectorCreated() {
        return httpConnector != null;
    }

    public boolean isHttpsConnectorCreated() {
        return httpsConnector != null;
    }

    public void setThreadPoolName(String s) {
        threadPool.setName(s);
    }

    public int getMaxFormContentSize() {
        return maxFormContentSize;
    }

    /**
     * Used to set the Jetty Server attribute "org.eclipse.jetty.server.Request.maxFormContentSize".
     * @param maxFormContentSize
     */
    public void setMaxFormContentSize(int maxFormContentSize) {
        this.maxFormContentSize = maxFormContentSize;
    }

    public void setHttpsForwarded(boolean httpsForwarded) {
        this.httpsForwarded = httpsForwarded;
    }

    public void setLowResourcesIdleTime(int lowResourcesIdleTime) {
        this.lowResourcesIdleTime = lowResourcesIdleTime;
    }

    public void setLowResourcesMaxTime(int lowResourcesMaxTime) {
        this.lowResourcesMaxTime = lowResourcesMaxTime;
    }

    public void setLowResourcesPeriod(int lowResourcesPeriod) {
        this.lowResourcesPeriod = lowResourcesPeriod;
    }

    public void setLowResourcesMaxConnections(int lowResourcesMaxConnections) {
        this.lowResourcesMaxConnections = lowResourcesMaxConnections;
    }

    public void setLowResourcesMaxMemory(long lowResourcesMaxMemory) {
        this.lowResourcesMaxMemory = lowResourcesMaxMemory;
    }

    public int getHttpAcceptQueueSize() {
        return httpAcceptQueueSize;
    }

    public void setHttpAcceptQueueSize(int httpAcceptQueueSize) {
        this.httpAcceptQueueSize = httpAcceptQueueSize;
    }

    public int getHttpsAcceptQueueSize() {
        return httpsAcceptQueueSize;
    }

    public void setHttpsAcceptQueueSize(int httpsAcceptQueueSize) {
        this.httpsAcceptQueueSize = httpsAcceptQueueSize;
    }

    public void setHttpsCertAlias(String httpsCertAlias) {
        this.httpsCertAlias = httpsCertAlias;
    }

    public long getLowResourcesMaxMemory() {
        return lowResourcesMaxMemory;
    }

    public int getLowResourcesMaxConnections() {
        return lowResourcesMaxConnections;
    }

    public int getLowResourcesPeriod() {
        return lowResourcesPeriod;
    }

    public int getLowResourcesMaxTime() {
        return lowResourcesMaxTime;
    }

    public int getLowResourcesIdleTime() {
        return lowResourcesIdleTime;
    }

    public boolean isLowResourcesMonitorThreads() {
        return lowResourcesMonitorThreads;
    }

    public void setLowResourcesMonitorThreads(boolean lowResourcesMonitorThreads) {
        this.lowResourcesMonitorThreads = lowResourcesMonitorThreads;
    }
}