Package com.betfair.cougar.transport.jetty

Source Code of com.betfair.cougar.transport.jetty.JettyHttpTransport

/*
* Copyright 2013, 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 com.betfair.cougar.api.export.Protocol;
import com.betfair.cougar.core.api.*;
import com.betfair.cougar.core.api.exception.CougarFrameworkException;
import com.betfair.cougar.core.api.exception.PanicInTheCougar;
import com.betfair.cougar.core.api.transports.AbstractRegisterableTransport;
import com.betfair.cougar.logging.CougarLogger;
import com.betfair.cougar.logging.CougarLoggingUtils;
import com.betfair.cougar.transport.api.RequestLogger;
import com.betfair.cougar.transport.api.TransportCommandProcessorFactory;
import com.betfair.cougar.transport.api.protocol.ProtocolBinding;
import com.betfair.cougar.transport.api.protocol.ProtocolBindingRegistry;
import com.betfair.cougar.transport.api.protocol.http.GeoLocationDeserializer;
import com.betfair.cougar.transport.api.protocol.http.HttpCommandProcessor;
import com.betfair.cougar.transport.api.protocol.http.HttpServiceBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.http.jsonrpc.JsonRpcOperationBindingDescriptor;
import com.betfair.cougar.transport.api.protocol.http.rescript.RescriptOperationBindingDescriptor;
import com.betfair.cougar.transport.jetty.jmx.JettyEndpoints;
import com.betfair.cougar.util.geolocation.GeoIPLocator;
import com.betfair.cougar.util.jmx.JMXControl;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedResource;

import javax.servlet.ServletException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.logging.Level;

@ManagedResource
public class JettyHttpTransport extends AbstractRegisterableTransport implements GateListener {
    private static final CougarLogger logger = CougarLoggingUtils.getLogger(JettyHttpTransport.class);

    private JMXControl jmxControl;

    private JettyServerWrapper server = new JettyServerWrapper();

    private StaticContentServiceHandler wsdlStaticHandler;
    private StaticContentServiceHandler htmlStaticHandler;
    private AliasHandler aliasHandler;
    private ContextHandlerCollection handlerCollection = new ContextHandlerCollection();

    private TransportCommandProcessorFactory<HttpCommandProcessor> commandProcessorFactory;
    private HttpCommandProcessor defaultCommandProcessor;
    private ProtocolBindingRegistry protocolBindingRegistry;

    private Map<String, JettyHandlerSpecification> handlerSpecificationMap = new HashMap<String, JettyHandlerSpecification>();

    private Map<String, String> pathAliases = new HashMap<>();

    // created in setApplicationContext
    // set to empty initially so tests that don't set up channels and service binding descriptors will work OK
    private Set<HttpServiceBindingDescriptor> serviceBindingDescriptors = new HashSet<HttpServiceBindingDescriptor>();

    // The set of JSON-RPC context roots already bound.
    private Set<String> rpcProtocolBindings = new HashSet<String>();

    // populated by setter methods

    private String wsdlContextPath;
    private String wsdlRegex;
    private String wsdlMediaType;

    private String htmlContextPath;
    private String htmlRegex;
    private String htmlMediaType;

    private GeoLocationDeserializer deserializer;
    private String uuidHeader;

    private int timeoutInSeconds;

    private GeoIPLocator geoIPLocator;

    private RequestLogger requestLogger;

    private JettyEndpoints jettyEndPoints;
   
    private boolean gzipEnabled;
    private int gzipMinSize;
    private int gzipBufferSize;
    private String gzipExcludedAgents;

    private int unknownCipherKeyLength;
    private boolean suppressCommasInAccessLogForStaticHtml;
    private boolean suppressCommasInAccessLogForCalls;

    public JettyHttpTransport() {
    }

    @Override
    public String getName() {
        return "JettyHttpTransport";
    }

    @Override
    public int getPriority() {
        return Integer.MIN_VALUE;
    }

    @Override
    public void onCougarStart() {
        createJettyHandlers();

        logOperationEndpoints(serviceBindingDescriptors);
        try {
            server.start();
        } catch (Exception ex) {
            logger.log(Level.SEVERE, "Failed to startup jetty", ex);
            throw new PanicInTheCougar(ex);
        }
    }

    /**
     * By setting the starting gate property this IntroductionService will register
     * itself with the CougarStartingGate
     *
     * @param startingGate the starting gate for the application
     */
    public void setStartingGate(CougarStartingGate startingGate) {
        startingGate.registerStartingListener(this);
    }

    @Override
    public void notify(BindingDescriptor bindingDescriptor) {
        if (bindingDescriptor.getServiceProtocol().underlyingTransportIsHttp()) {
            serviceBindingDescriptors.add((HttpServiceBindingDescriptor) bindingDescriptor);
            registerHandler((HttpServiceBindingDescriptor) bindingDescriptor);
        }
    }

    public void start() throws Exception {
        register();
        initialiseStaticJettyConfig();
        server.startThreadPool();

        // Create a shutdown hook to close the jetty port cleanly
        Runtime.getRuntime().addShutdownHook(new Thread("Jetty Shutdown Hook") {

            @Override
            public void run() {
                logger.log(Level.INFO, "Gracefully shutting down Jetty Server");
                try {
                    JettyHttpTransport.this.stop();
                } catch (Exception e) {
                    logger.log(Level.WARNING, "Failed to shutdown jetty", e);
                }
            }
        });
    }

    public void stop() throws Exception {
        unregister();
        server.stop();
    }

    public void initialiseStaticJettyConfig() throws Exception {
        server.initialiseConnectors();

        ErrorHandler errorHandler = new CougarErrorHandler();
        wsdlStaticHandler = new StaticContentServiceHandler(
                wsdlContextPath,
                wsdlRegex,
                wsdlMediaType,
                uuidHeader,
                deserializer,
                geoIPLocator,
                requestLogger,
                true);
        wsdlStaticHandler.setUnknownCipherKeyLength(unknownCipherKeyLength);

        htmlStaticHandler = new StaticContentServiceHandler(
                htmlContextPath,
                htmlRegex,
                htmlMediaType,
                uuidHeader,
                deserializer,
                geoIPLocator,
                requestLogger,
                suppressCommasInAccessLogForStaticHtml);
        htmlStaticHandler.setUnknownCipherKeyLength(unknownCipherKeyLength);

//        aliasHandler = new AliasHandler(pathAliases);

        StatisticsHandler statisticsHandler = new StatisticsHandler();
        statisticsHandler.setServer(server.getJettyServer());

        handlerCollection.setServer(server.getJettyServer());

        JettyHandler defaultJettyServiceHandler = new AliasHandler(defaultCommandProcessor, suppressCommasInAccessLogForCalls, pathAliases);
        ContextHandler context = new ContextHandler();
        context.setContextPath("");
        context.setResourceBase(".");
        context.setHandler(defaultJettyServiceHandler);
        handlerCollection.addHandler(context);

        handlerCollection.addHandler(wsdlStaticHandler);
        handlerCollection.addHandler(htmlStaticHandler);
//        handlerCollection.addHandler(aliasHandler);
        statisticsHandler.setHandler(handlerCollection);

        // Register the errorhandler with the server itself
        server.addBean(errorHandler);
        server.setHandler(statisticsHandler);
    }


    /**
     * This method adds the service binding descriptor, and all the appropriate protocol binding combos
     * into the handlerSpecMap, before binding the serviceBindingDescriptor to the appropriate
     * command processor
     */
    public void registerHandler(HttpServiceBindingDescriptor serviceBindingDescriptor) {
        for (ProtocolBinding protocolBinding : protocolBindingRegistry.getProtocolBindings()) {
            if (protocolBinding.getProtocol() == serviceBindingDescriptor.getServiceProtocol()) {
                String contextPath = protocolBinding.getContextRoot() + serviceBindingDescriptor.getServiceContextPath();

                JettyHandlerSpecification handlerSpec = handlerSpecificationMap.get(contextPath);
                if (handlerSpec == null) {
                    handlerSpec = new JettyHandlerSpecification(protocolBinding.getContextRoot(),
                            protocolBinding.getProtocol(), serviceBindingDescriptor.getServiceContextPath());
                    handlerSpecificationMap.put(contextPath, handlerSpec);
                }
                if (protocolBinding.getIdentityTokenResolver() != null) {
                    handlerSpec.addServiceVersionToTokenResolverEntry(serviceBindingDescriptor.getServiceVersion(), protocolBinding.getIdentityTokenResolver());
                }
            }
        }
        commandProcessorFactory.getCommandProcessor(serviceBindingDescriptor.getServiceProtocol()).bind(serviceBindingDescriptor);
    }

    private void createJettyHandlers() {
        for (Map.Entry<String, JettyHandlerSpecification> spec : handlerSpecificationMap.entrySet()) {
            HttpCommandProcessor commandProcessor = commandProcessorFactory.getCommandProcessor(spec.getValue().getProtocol());
            bindServiceContextRoot(spec.getValue(), commandProcessor);
        }
    }

    private void bindServiceContextRoot(JettyHandlerSpecification spec, HttpCommandProcessor commandProcessor) {
        Protocol serviceProtocol = spec.getProtocol();
        String jettyContextRoot = spec.getJettyContextRoot();
        if (jettyContextRoot.endsWith("/")) {
            jettyContextRoot = jettyContextRoot.substring(0, jettyContextRoot.length()-1);
        }

        if (serviceProtocol == Protocol.JSON_RPC && jsonRpcContextAlreadyBound(jettyContextRoot)) {
            logger.log(Level.INFO, "Not binding Jetty Handler for protocol: JSON-RPC on context root [" +
                    jettyContextRoot + "] - context already bound");
        } else {
            StringBuilder sb = new StringBuilder("Adding a new Jetty Handler on url [").append(jettyContextRoot).append("] ");
            if (serviceProtocol == Protocol.JSON_RPC) {
                sb.append("For all service/version combinations ");
            } else {
                sb.append("for versions [");
                for (ServiceVersion version : spec.getVersionToIdentityTokenResolverMap().keySet()) {
                    sb.append(version.toString() + " ");
                }
                sb.append("] ");
            }
            sb.append("on protocol " + serviceProtocol);
            logger.log(Level.INFO, sb.toString());

            JettyHandler.IdentityTokenResolverLookup identityTokenResolverLookup = null;

            if (!spec.getVersionToIdentityTokenResolverMap().isEmpty()) {
                if (spec.getVersionToIdentityTokenResolverMap().size() == 1) {
                    identityTokenResolverLookup =
                            new JettyHandler.SingletonIdentityTokenResolverLookup(
                                    spec.getVersionToIdentityTokenResolverMap().values().iterator().next());
                } else {
                    identityTokenResolverLookup = new JettyHandler.GeneralHttpIdentityTokenResolverLookup(jettyContextRoot, spec);
                }
            }
            JettyHandler jettyServiceHandler = new JettyHandler(commandProcessor, spec, identityTokenResolverLookup, suppressCommasInAccessLogForCalls);
            jettyServiceHandler.setTimeoutInSeconds(timeoutInSeconds);
            // Jetty stuff
            ContextHandler context = new ContextHandler();
            context.setServer(server.getJettyServer());
            context.setContextPath(jettyContextRoot);

            // Rescript is not allowed null paths as the operation must be appended to the end of the URI
            if (serviceProtocol == Protocol.SOAP || serviceProtocol == Protocol.JSON_RPC) {
                context.setAllowNullPathInfo(true);
            } else {
                context.setAllowNullPathInfo(false);
            }
            context.setResourceBase(".");
            if (gzipEnabled) {
                try {
                    context.setHandler(new GzipHandler(gzipBufferSize,gzipMinSize,gzipExcludedAgents, jettyServiceHandler));
                }
                catch (ServletException e) {
                    throw new CougarFrameworkException("Failed to create GZIP handler: [" + jettyContextRoot + "]", e);
                }
            }
            else {
                context.setHandler(jettyServiceHandler);
            }
            handlerCollection.addHandler(context);
            try {
                //If the server is already running, start this context up!
                //If it isn't running, then it will be started when Jetty starts
                if (server.isRunning()) {
                    context.start();
                }
            } catch (Exception ex) {
                throw new CougarFrameworkException("Failed to register serviceBindingDescriptor: [" + jettyContextRoot + "]", ex);
            }
        }
    }

    private boolean jsonRpcContextAlreadyBound(String contextRoot) {
        if (rpcProtocolBindings.contains(contextRoot)) {
            return true;
        }
        rpcProtocolBindings.add(contextRoot);
        return false;
    }

    private void logOperationEndpoints(
            Collection<HttpServiceBindingDescriptor> services
    ) {
        List<String> entries = new ArrayList<String>();
        for (ProtocolBinding protocolBinding : protocolBindingRegistry.getProtocolBindings()) {
            for (HttpServiceBindingDescriptor service : services) {
                if (service.getServiceProtocol() == protocolBinding.getProtocol()) {
                    OperationBindingDescriptor[] operations = service.getOperationBindings();
                    for (OperationBindingDescriptor operation : operations) {
                        StringBuffer entry = new StringBuffer();
                        entry.append(String.format("%9s : ", service.getServiceProtocol()));
                        entry.append(String.valueOf(operation.getOperationKey()));
                        entry.append(" => ");
                        entry.append(endpointFor(protocolBinding.getContextRoot(), service, operation));
                        entries.add(entry.toString());
                    }
                }
            }
        }
        Collections.sort(entries);
        jettyEndPoints.setEndPoints(entries);
    }

    private String endpointFor(String contextRoot, HttpServiceBindingDescriptor serviceDescriptor,
                               OperationBindingDescriptor operationDescriptor
    ) {
        StringBuffer buf = new StringBuffer();
        buf.append(getURISchemeAndAuthority());
        buf.append(contextRoot);
        buf.append(serviceDescriptor.getServiceContextPath());
        if (serviceDescriptor.getServiceProtocol() != Protocol.JSON_RPC) {
            buf.append("v").append(serviceDescriptor.getServiceVersion().getMajor());
        }
        if (operationDescriptor instanceof RescriptOperationBindingDescriptor) {
            String path = ((RescriptOperationBindingDescriptor) operationDescriptor).getURI();
            buf.append(path);
        } else if (operationDescriptor instanceof JsonRpcOperationBindingDescriptor){
            buf.append("/");
        }
        return buf.toString();
    }

    private String getURISchemeAndAuthority() {
        try {
            boolean http = server.isHttpEnabled();
            return String.format(
                    "%s://%s:%d",
                    http ? "http" : "https",
                    InetAddress.getLocalHost().getHostAddress(),
                    http ? server.getHttpPort() : server.getHttpsPort()
            );
        } catch (UnknownHostException e) {
            throw new RuntimeException("There was a problem determining this host's address", e);
        }
    }

    public void setServerWrapper(JettyServerWrapper server) {
        this.server = server;
    }

    public JettyServerWrapper getServerWrapper() {
        return server;
    }

    public void setDefaultCommandProcessor(HttpCommandProcessor defaultCommandProcessor) {
        this.defaultCommandProcessor = defaultCommandProcessor;
    }

    public void setProtocolBindingRegistry(ProtocolBindingRegistry protocolBindingRegistry) {
        this.protocolBindingRegistry = protocolBindingRegistry;
    }

    public void setTimeoutInSeconds(int timeoutInSeconds) {
        this.timeoutInSeconds = timeoutInSeconds;
    }

    public Server getJettyServer() {
        return server.getJettyServer();
    }

    public StaticContentServiceHandler getWsdlStaticHandler() {
        return wsdlStaticHandler;
    }

    public StaticContentServiceHandler getHtmlStaticHandler() {
        return htmlStaticHandler;
    }

    public Map<String, JettyHandlerSpecification> getHandlerSpecificationMap() {
        return handlerSpecificationMap;
    }

    public void setCommandProcessorFactory(TransportCommandProcessorFactory<HttpCommandProcessor> commandProcessorFactory) {
        this.commandProcessorFactory = commandProcessorFactory;
    }


    public void setWsdlContextPath(String wsdlContextPath) {
        this.wsdlContextPath = wsdlContextPath;
    }

    public void setWsdlRegex(String wsdlRegex) {
        this.wsdlRegex = wsdlRegex;
    }

    public void setWsdlMediaType(String wsdlMediaType) {
        this.wsdlMediaType = wsdlMediaType;
    }

    public void setHtmlContextPath(String htmlContextPath) {
        this.htmlContextPath = htmlContextPath;
    }

    public void setHtmlRegex(String htmlRegex) {
        this.htmlRegex = htmlRegex;
    }

    public void setHtmlMediaType(String htmlMediaType) {
        this.htmlMediaType = htmlMediaType;
    }

    public void setGeoLocationDeserializer(GeoLocationDeserializer deserializer) {
        this.deserializer = deserializer;
    }

    public void setRequestLogger(RequestLogger requestLogger) {
        this.requestLogger = requestLogger;
    }

    public void setUuidHeader(String uuidHeader) {
        this.uuidHeader = uuidHeader;
    }

    public void setGeoIPLocator(GeoIPLocator geoIPLocator) {
        this.geoIPLocator = geoIPLocator;
    }

    public void setJettyEndPoints(JettyEndpoints jettyEndPoints) {
        this.jettyEndPoints = jettyEndPoints;
    }

    public void setJmxControl(JMXControl jmxControl) {
        this.jmxControl = jmxControl;
    }

    public void setPathAliases(Map<String, String> pathAliases) {
        this.pathAliases = pathAliases;
    }

    @ManagedAttribute
    public Map<String, String> getPathAliases() {
        return pathAliases;
    }

    @ManagedAttribute
    public boolean isHttpEnabled() {
        return server.isHttpEnabled();
    }

    @ManagedAttribute
    public boolean isHttpsEnabled() {
        return server.isHttpsEnabled();
    }

    @ManagedAttribute
    public int getRequestHeaderSize() {
        return server.getRequestHeaderSize();
    }

    @ManagedAttribute
    public int getResponseBufferSize() {
        return server.getResponseBufferSize();
    }

    @ManagedAttribute
    public int getResponseHeaderSize() {
        return server.getResponseHeaderSize();
    }

    @ManagedAttribute
    public int getHttpAcceptors() {
        return server.getHttpAcceptors();
    }

    @ManagedAttribute
    public int getHttpSelectors() {
        return server.getHttpSelectors();
    }

    @ManagedAttribute
    public int getHttpsAcceptors() {
        return server.getHttpsAcceptors();
    }

    @ManagedAttribute
    public int getHttpsSelectors() {
        return server.getHttpsSelectors();
    }

    @ManagedAttribute
    public boolean isHttpForwarded() {
        return server.isHttpForwarded();
    }

    @ManagedAttribute
    public boolean isHttpsForwarded() {
        return server.isHttpsForwarded();
    }

    @ManagedAttribute
    public boolean isGzipEnabled() {
      return gzipEnabled;
    }
   
    public void setGzipEnabled(boolean gzipEnabled) {
      this.gzipEnabled = gzipEnabled;
    }
   
    @ManagedAttribute
    public String getExcludedAgents() {
      return gzipExcludedAgents;
    }
   
    public void setGzipExcludedAgents(String excludedAgents) {
      if (excludedAgents != null && !excludedAgents.isEmpty()) {
        this.gzipExcludedAgents = excludedAgents;
      }
    }
   
    @ManagedAttribute
    public int getGzipBufferSize() {
      return gzipBufferSize;
    }
   
    public void setGzipBufferSize(int bufferSize) {
      this.gzipBufferSize = bufferSize;
    }
   
    @ManagedAttribute
    public int getGzipMinSize() {
      return gzipMinSize;
    }
   
    public void setGzipMinSize(int minSize) {
      this.gzipMinSize = minSize;
    }


    @ManagedAttribute
    public int getMaxFormContentSize() {
        return getServerWrapper().getMaxFormContentSize();
    }

    @ManagedAttribute
    public int getUnknownCipherKeyLength() {
        return unknownCipherKeyLength;
    }

    public void setUnknownCipherKeyLength(int unknownCipherKeyLength) {
        this.unknownCipherKeyLength = unknownCipherKeyLength;
    }

    @ManagedAttribute
    public int getHttpAcceptQueueSize() {
        return getServerWrapper().getHttpAcceptQueueSize();
    }

    @ManagedAttribute
    public int getHttpsAcceptQueueSize() {
        return getServerWrapper().getHttpsAcceptQueueSize();
    }

    @ManagedAttribute
    public long getLowResourcesMaxMemory() {
        return getServerWrapper().getLowResourcesMaxMemory();
    }

    @ManagedAttribute
    public int getLowResourcesMaxConnections() {
        return getServerWrapper().getLowResourcesMaxConnections();
    }

    @ManagedAttribute
    public int getLowResourcesPeriod() {
        return getServerWrapper().getLowResourcesPeriod();
    }

    @ManagedAttribute
    public int getLowResourcesMaxTime() {
        return getServerWrapper().getLowResourcesMaxTime();
    }

    @ManagedAttribute
    public int getLowResourcesIdleTime() {
        return getServerWrapper().getLowResourcesIdleTime();
    }

    @ManagedAttribute
    public boolean isLowResourcesMonitorThreads() {
        return getServerWrapper().isLowResourcesMonitorThreads();
    }

    @ManagedAttribute
    public boolean isSuppressCommasInAccessLogForStaticHtml() {
        return suppressCommasInAccessLogForStaticHtml;
    }

    public void setSuppressCommasInAccessLogForStaticHtml(boolean suppressCommasInAccessLogForStaticHtml) {
        this.suppressCommasInAccessLogForStaticHtml = suppressCommasInAccessLogForStaticHtml;
    }

    @ManagedAttribute
    public boolean isSuppressCommasInAccessLogForCalls() {
        return suppressCommasInAccessLogForCalls;
    }

    public void setSuppressCommasInAccessLogForCalls(boolean suppressCommasInAccessLogForCalls) {
        this.suppressCommasInAccessLogForCalls = suppressCommasInAccessLogForCalls;
    }
}
TOP

Related Classes of com.betfair.cougar.transport.jetty.JettyHttpTransport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.