blob: 6d273834e4bd76c92fddb335d4cbe93f552f12c4 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 org.apache.cloudstack;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.Properties;
import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.RequestLog;
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.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.MovedContextHandler;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloud.utils.PropertiesUtil;
import com.google.common.base.Strings;
/***
* The ServerDaemon class implements the embedded server, it can be started either
* using JSVC or directly from the JAR along with additional jars not shaded in the uber-jar.
* Configuration parameters are read from server.properties file available on the classpath.
*/
public class ServerDaemon implements Daemon {
private static final Logger LOG = LoggerFactory.getLogger(ServerDaemon.class);
private static final String WEB_XML = "META-INF/webapp/WEB-INF/web.xml";
/////////////////////////////////////////////////////
/////////////// Server Properties ///////////////////
/////////////////////////////////////////////////////
private static final String BIND_INTERFACE = "bind.interface";
private static final String CONTEXT_PATH = "context.path";
private static final String SESSION_TIMEOUT = "session.timeout";
private static final String HTTP_PORT = "http.port";
private static final String HTTPS_ENABLE = "https.enable";
private static final String HTTPS_PORT = "https.port";
private static final String KEYSTORE_FILE = "https.keystore";
private static final String KEYSTORE_PASSWORD = "https.keystore.password";
private static final String WEBAPP_DIR = "webapp.dir";
private static final String ACCESS_LOG = "access.log";
////////////////////////////////////////////////////////
/////////////// Server Configuration ///////////////////
////////////////////////////////////////////////////////
private Server server;
private int httpPort = 8080;
private int httpsPort = 8443;
private int sessionTimeout = 30;
private boolean httpsEnable = false;
private String accessLogFile = "access.log";
private String bindInterface = null;
private String contextPath = "/client";
private String keystoreFile;
private String keystorePassword;
private String webAppLocation;
//////////////////////////////////////////////////
/////////////// Public methods ///////////////////
//////////////////////////////////////////////////
public static void main(final String... anArgs) throws Exception {
final ServerDaemon daemon = new ServerDaemon();
daemon.init(null);
daemon.start();
}
@Override
public void init(final DaemonContext context) {
final File confFile = PropertiesUtil.findConfigFile("server.properties");
if (confFile == null) {
LOG.warn(String.format("Server configuration file not found. Initializing server daemon on %s:%s, with https.enabled=%s, https.port=%s, context.path=%s",
bindInterface, httpPort, httpsEnable, httpsPort, contextPath));
return;
}
LOG.info("Server configuration file found: " + confFile.getAbsolutePath());
try {
final Properties properties = PropertiesUtil.loadFromFile(confFile);
if (properties == null) {
return;
}
setBindInterface(properties.getProperty(BIND_INTERFACE, null));
setContextPath(properties.getProperty(CONTEXT_PATH, "/client"));
setHttpPort(Integer.valueOf(properties.getProperty(HTTP_PORT, "8080")));
setHttpsEnable(Boolean.valueOf(properties.getProperty(HTTPS_ENABLE, "false")));
setHttpsPort(Integer.valueOf(properties.getProperty(HTTPS_PORT, "8443")));
setKeystoreFile(properties.getProperty(KEYSTORE_FILE));
setKeystorePassword(properties.getProperty(KEYSTORE_PASSWORD));
setWebAppLocation(properties.getProperty(WEBAPP_DIR));
setAccessLogFile(properties.getProperty(ACCESS_LOG, "access.log"));
setSessionTimeout(Integer.valueOf(properties.getProperty(SESSION_TIMEOUT, "30")));
} catch (final IOException e) {
LOG.warn("Failed to load configuration from server.properties file", e);
}
LOG.info(String.format("Initializing server daemon on %s:%s, with https.enabled=%s, https.port=%s, context.path=%s",
bindInterface, httpPort, httpsEnable, httpsPort, contextPath));
}
@Override
public void start() throws Exception {
// Thread pool
final QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setMinThreads(10);
threadPool.setMaxThreads(500);
// Jetty Server
server = new Server(threadPool);
// Setup Scheduler
server.addBean(new ScheduledExecutorScheduler());
// Setup JMX
final MBeanContainer mbeanContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
server.addBean(mbeanContainer);
// HTTP config
final HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.addCustomizer( new ForwardedRequestCustomizer() );
httpConfig.setSecureScheme("https");
httpConfig.setSecurePort(httpsPort);
httpConfig.setOutputBufferSize(32768);
httpConfig.setRequestHeaderSize(8192);
httpConfig.setResponseHeaderSize(8192);
httpConfig.setSendServerVersion(false);
httpConfig.setSendDateHeader(false);
// HTTP Connector
final ServerConnector httpConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
httpConnector.setPort(httpPort);
httpConnector.setHost(bindInterface);
httpConnector.setIdleTimeout(30000);
server.addConnector(httpConnector);
// Setup handlers
server.setHandler(createHandlers());
// Extra config options
server.setStopAtShutdown(true);
// Configure SSL
if (httpsEnable && !Strings.isNullOrEmpty(keystoreFile) && new File(keystoreFile).exists()) {
// SSL Context
final SslContextFactory sslContextFactory = new SslContextFactory();
// Define keystore path and passwords
sslContextFactory.setKeyStorePath(keystoreFile);
sslContextFactory.setKeyStorePassword(keystorePassword);
sslContextFactory.setKeyManagerPassword(keystorePassword);
// HTTPS config
final HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.addCustomizer(new SecureRequestCustomizer());
// HTTPS connector
final ServerConnector sslConnector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, "http/1.1"),
new HttpConnectionFactory(httpsConfig));
sslConnector.setPort(httpsPort);
sslConnector.setHost(bindInterface);
server.addConnector(sslConnector);
}
server.start();
server.join();
}
@Override
public void stop() throws Exception {
server.stop();
}
@Override
public void destroy() {
server.destroy();
}
///////////////////////////////////////////////////
/////////////// Private methods ///////////////////
///////////////////////////////////////////////////
private HandlerCollection createHandlers() {
final WebAppContext webApp = new WebAppContext();
webApp.setContextPath(contextPath);
webApp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
webApp.getSessionHandler().setMaxInactiveInterval(sessionTimeout * 60);
// GZIP handler
final GzipHandler gzipHandler = new GzipHandler();
gzipHandler.addIncludedMimeTypes("text/html", "text/xml", "text/css", "text/plain", "text/javascript", "application/javascript", "application/json", "application/xml");
gzipHandler.setIncludedMethods("GET", "POST");
gzipHandler.setCompressionLevel(9);
gzipHandler.setHandler(webApp);
if (Strings.isNullOrEmpty(webAppLocation)) {
webApp.setWar(getShadedWarUrl());
} else {
webApp.setWar(webAppLocation);
}
// Request log handler
final RequestLogHandler log = new RequestLogHandler();
log.setRequestLog(createRequestLog());
// Redirect root context handler
MovedContextHandler rootRedirect = new MovedContextHandler();
rootRedirect.setContextPath("/");
rootRedirect.setNewContextURL(contextPath);
rootRedirect.setPermanent(true);
// Put rootRedirect at the end!
return new HandlerCollection(log, gzipHandler, rootRedirect);
}
private RequestLog createRequestLog() {
final NCSARequestLog log = new NCSARequestLog();
final File logPath = new File(accessLogFile);
final File parentFile = logPath.getParentFile();
if (parentFile != null) {
parentFile.mkdirs();
}
log.setFilename(logPath.getPath());
log.setAppend(true);
log.setLogTimeZone("GMT");
log.setLogLatency(true);
return log;
}
private URL getResource(String aResource) {
return Thread.currentThread().getContextClassLoader().getResource(aResource);
}
private String getShadedWarUrl() {
final String urlStr = getResource(WEB_XML).toString();
return urlStr.substring(0, urlStr.length() - 15);
}
///////////////////////////////////////////
/////////////// Setters ///////////////////
///////////////////////////////////////////
public void setBindInterface(String bindInterface) {
this.bindInterface = bindInterface;
}
public void setHttpPort(int httpPort) {
this.httpPort = httpPort;
}
public void setHttpsPort(int httpsPort) {
this.httpsPort = httpsPort;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public void setHttpsEnable(boolean httpsEnable) {
this.httpsEnable = httpsEnable;
}
public void setKeystoreFile(String keystoreFile) {
this.keystoreFile = keystoreFile;
}
public void setKeystorePassword(String keystorePassword) {
this.keystorePassword = keystorePassword;
}
public void setAccessLogFile(String accessLogFile) {
this.accessLogFile = accessLogFile;
}
public void setWebAppLocation(String webAppLocation) {
this.webAppLocation = webAppLocation;
}
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
}