org.sonar.server.app.TomcatConnectors.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.server.app.TomcatConnectors.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.app;

import static com.google.common.collect.FluentIterable.from;
import static java.util.Arrays.asList;

import com.google.common.base.Predicates;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.sonar.process.Props;

/**
 * Configuration of Tomcat connectors
 */
class TomcatConnectors {

    public static final int DISABLED_PORT = -1;
    public static final String HTTP_PROTOCOL = "HTTP/1.1";
    public static final String AJP_PROTOCOL = "AJP/1.3";
    public static final int MAX_HTTP_HEADER_SIZE_BYTES = 48 * 1024;
    private static final int MAX_POST_SIZE = -1;

    private TomcatConnectors() {
        // only static stuff
    }

    static void configure(Tomcat tomcat, Props props) {
        List<Connector> connectors = from(asList(newHttpConnector(props), newAjpConnector(props)))
                .filter(Predicates.notNull()).toList();

        verify(connectors);

        tomcat.setConnector(connectors.get(0));
        for (Connector connector : connectors) {
            tomcat.getService().addConnector(connector);
        }
    }

    private static void verify(List<Connector> connectors) {
        if (connectors.isEmpty()) {
            throw new IllegalStateException("HTTP connectors are disabled");
        }
        Set<Integer> ports = new HashSet<>();
        for (Connector connector : connectors) {
            int port = connector.getPort();
            if (ports.contains(port)) {
                throw new IllegalStateException(String.format("HTTP and AJP must not use the same port %d", port));
            }
            ports.add(port);
        }
    }

    @CheckForNull
    private static Connector newHttpConnector(Props props) {
        Connector connector = null;
        // Not named "sonar.web.http.port" to keep backward-compatibility
        int port = props.valueAsInt("sonar.web.port", 9000);
        if (port > DISABLED_PORT) {
            connector = newConnector(props, HTTP_PROTOCOL, "http");
            configureMaxHttpHeaderSize(connector);
            connector.setPort(port);
            connector.setMaxPostSize(MAX_POST_SIZE);
        }
        return connector;
    }

    @CheckForNull
    private static Connector newAjpConnector(Props props) {
        int port = props.valueAsInt("sonar.ajp.port", DISABLED_PORT);
        if (port > DISABLED_PORT) {
            Connector connector = newConnector(props, AJP_PROTOCOL, "http");
            connector.setPort(port);
            return connector;
        }
        return null;
    }

    /**
     * HTTP header must be at least 48kb  to accommodate the authentication token used for
     * negotiate protocol of windows authentication.
     */
    private static void configureMaxHttpHeaderSize(Connector connector) {
        setConnectorAttribute(connector, "maxHttpHeaderSize", MAX_HTTP_HEADER_SIZE_BYTES);
    }

    private static Connector newConnector(Props props, String protocol, String scheme) {
        Connector connector = new Connector(protocol);
        connector.setURIEncoding("UTF-8");
        connector.setProperty("address", props.value("sonar.web.host", "0.0.0.0"));
        connector.setProperty("socket.soReuseAddress", "true");
        configurePool(props, connector, scheme);
        configureCompression(connector);
        return connector;
    }

    private static void configurePool(Props props, Connector connector, String scheme) {
        connector.setProperty("acceptorThreadCount", String.valueOf(2));
        connector.setProperty("minSpareThreads",
                String.valueOf(props.valueAsInt("sonar.web." + scheme + ".minThreads", 5)));
        connector.setProperty("maxThreads",
                String.valueOf(props.valueAsInt("sonar.web." + scheme + ".maxThreads", 50)));
        connector.setProperty("acceptCount",
                String.valueOf(props.valueAsInt("sonar.web." + scheme + ".acceptCount", 25)));
    }

    private static void configureCompression(Connector connector) {
        connector.setProperty("compression", "on");
        connector.setProperty("compressionMinSize", "1024");
        connector.setProperty("compressableMimeType",
                "text/html,text/xml,text/plain,text/css,application/json,application/javascript");
    }

    private static void setConnectorAttribute(Connector c, String key, @Nullable Object value) {
        if (value != null) {
            c.setAttribute(key, value);
        }
    }
}