blob: 5b123e91200d748d5ce87936afe93c00ed301c2a [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.oozie.server;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.ProvisionException;
import org.apache.hadoop.conf.Configuration;
import org.apache.oozie.server.guice.OozieGuiceModule;
import org.apache.oozie.service.ConfigurationService;
import org.apache.oozie.service.ServiceException;
import org.apache.oozie.service.Services;
import org.apache.oozie.util.ConfigUtils;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Objects;
/**
* Class to start Oozie inside an embedded Jetty server.
*/
public class EmbeddedOozieServer {
private static final Logger LOG = LoggerFactory.getLogger(EmbeddedOozieServer.class);
protected static final String OOZIE_HTTPS_TRUSTSTORE_FILE = "oozie.https.truststore.file";
protected static final String OOZIE_HTTPS_TRUSTSTORE_TYPE = "oozie.https.truststore.type";
protected static final String OOZIE_HTTPS_TRUSTSTORE_PASS = "oozie.https.truststore.pass";
protected static final String TRUSTSTORE_PATH_SYSTEM_PROPERTY = "javax.net.ssl.trustStore";
protected static final String TRUSTSTORE_TYPE_SYSTEM_PROPERTY = "javax.net.ssl.trustStoreType";
protected static final String TRUSTSTORE_PASS_SYSTEM_PROPERTY = "javax.net.ssl.trustStorePassword";
private static String contextPath;
protected Server server;
private int httpPort;
private int httpsPort;
private final WebAppContext servletContextHandler;
private final ServletMapper oozieServletMapper;
private final FilterMapper oozieFilterMapper;
private JspHandler jspHandler;
private Services serviceController;
private SSLServerConnectorFactory sslServerConnectorFactory;
private Configuration conf;
private final RewriteHandler oozieRewriteHandler;
private final ConstraintSecurityHandler constraintSecurityHandler;
/**
* Construct Oozie server
* @param server jetty server to be embedded
* @param jspHandler handler responsible for setting webapp context for JSP
* @param serviceController controller for Oozie services; must be already initialized
* @param sslServerConnectorFactory factory to create server connector configured for SSL
* @param oozieRewriteHandler URL rewriter
* @param servletContextHandler main web application context handler
* @param oozieServletMapper maps servlets to URLs
* @param oozieFilterMapper maps filters
* @param constraintSecurityHandler constraint security handler
*/
@Inject
public EmbeddedOozieServer(final Server server,
final JspHandler jspHandler,
final Services serviceController,
final SSLServerConnectorFactory sslServerConnectorFactory,
final RewriteHandler oozieRewriteHandler,
final WebAppContext servletContextHandler,
final ServletMapper oozieServletMapper,
final FilterMapper oozieFilterMapper,
final ConstraintSecurityHandler constraintSecurityHandler)
{
this.constraintSecurityHandler = constraintSecurityHandler;
this.serviceController = Objects.requireNonNull(serviceController, "serviceController is null");
this.jspHandler = Objects.requireNonNull(jspHandler, "jspHandler is null");
this.sslServerConnectorFactory = Objects.requireNonNull(sslServerConnectorFactory,
"sslServerConnectorFactory is null");
this.server = Objects.requireNonNull(server, "server is null");
this.oozieRewriteHandler = Objects.requireNonNull(oozieRewriteHandler, "rewriter is null");
this.servletContextHandler = Objects.requireNonNull(servletContextHandler, "servletContextHandler is null");
this.oozieServletMapper = Objects.requireNonNull(oozieServletMapper, "oozieServletMapper is null");
this.oozieFilterMapper = Objects.requireNonNull(oozieFilterMapper, "oozieFilterMapper is null");
}
/**
* Set up the Oozie server by configuring jetty server settings and starts Oozie services
*
* @throws URISyntaxException if the server URI is not well formatted
* @throws IOException in case of IO issues
* @throws ServiceException if the server could not start
*/
public void setup() throws URISyntaxException, IOException, ServiceException {
conf = serviceController.get(ConfigurationService.class).getConf();
setContextPath(conf);
httpPort = getConfigPort(ConfigUtils.OOZIE_HTTP_PORT);
HttpConfiguration httpConfiguration = new HttpConfigurationWrapper(conf).getDefaultHttpConfiguration();
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
connector.setPort(httpPort);
connector.setHost(conf.get(ConfigUtils.OOZIE_HTTP_HOSTNAME));
HandlerCollection handlerCollection = new HandlerCollection();
setTrustStore();
setTrustStoreType();
setTrustStorePassword();
if (isSecured()) {
httpsPort = getConfigPort(ConfigUtils.OOZIE_HTTPS_PORT);
ServerConnector sslConnector = sslServerConnectorFactory.createSecureServerConnector(httpsPort, conf, server);
server.setConnectors(new Connector[]{connector, sslConnector});
constraintSecurityHandler.setHandler(servletContextHandler);
handlerCollection.addHandler(constraintSecurityHandler);
}
else {
server.setConnectors(new Connector[]{connector});
}
servletContextHandler.setContextPath(contextPath);
oozieServletMapper.mapOozieServlets();
oozieFilterMapper.addFilters();
servletContextHandler.setParentLoaderPriority(true);
jspHandler.setupWebAppContext(servletContextHandler);
addErrorHandler();
handlerCollection.addHandler(servletContextHandler);
handlerCollection.addHandler(oozieRewriteHandler);
server.setHandler(handlerCollection);
}
/**
* set the truststore path from the config file, if is not set by the user
*/
private void setTrustStore() {
if (System.getProperty(TRUSTSTORE_PATH_SYSTEM_PROPERTY) == null) {
final String trustStorePath = conf.get(OOZIE_HTTPS_TRUSTSTORE_FILE);
if (trustStorePath != null) {
LOG.info("Setting javax.net.ssl.trustStore from config file");
System.setProperty(TRUSTSTORE_PATH_SYSTEM_PROPERTY, trustStorePath);
}
} else {
LOG.info("javax.net.ssl.trustStore is already set. The value from config file will be ignored");
}
}
/**
* set the truststore type from the config file, if is not set by the user
*/
private void setTrustStoreType() {
if (System.getProperty(TRUSTSTORE_TYPE_SYSTEM_PROPERTY) == null) {
final String trustStoreType = conf.get(OOZIE_HTTPS_TRUSTSTORE_TYPE);
if (trustStoreType != null) {
LOG.info("Setting javax.net.ssl.trustStoreType from config file");
System.setProperty(TRUSTSTORE_TYPE_SYSTEM_PROPERTY, trustStoreType);
}
} else {
LOG.info("javax.net.ssl.trustStoreType is already set. The value from config file will be ignored");
}
}
/**
* set the truststore password from the config file, if is not set by the user
*/
private void setTrustStorePassword() {
if (System.getProperty(TRUSTSTORE_PASS_SYSTEM_PROPERTY) == null) {
final String trustStorePassword = conf.get(OOZIE_HTTPS_TRUSTSTORE_PASS);
if (trustStorePassword != null) {
LOG.info("Setting javax.net.ssl.trustStorePassword from config file");
System.setProperty(TRUSTSTORE_PASS_SYSTEM_PROPERTY, trustStorePassword);
}
} else {
LOG.info("javax.net.ssl.trustStorePassword is already set. The value from config file will be ignored");
}
}
private void addErrorHandler() {
ErrorPageErrorHandler errorHandler = new ErrorPageErrorHandler();
errorHandler.addErrorPage(HttpServletResponse.SC_BAD_REQUEST, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_UNAUTHORIZED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_FORBIDDEN, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_FOUND, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_CONFLICT, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_NOT_IMPLEMENTED, "/error");
errorHandler.addErrorPage(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "/error");
errorHandler.addErrorPage("java.lang.Throwable", "/error");
servletContextHandler.setErrorHandler(errorHandler);
}
private int getConfigPort(String confVar) {
String confHttpPort = conf.get(confVar);
int port;
try {
port = Integer.parseInt(confHttpPort);
}
catch (final NumberFormatException nfe) {
throw new NumberFormatException(String.format("Port number for '%s \"%s\" ('%s') is not an integer.",
confVar, confHttpPort, confHttpPort));
}
return port;
}
private boolean isSecured() {
boolean isSSLEnabled = Boolean.parseBoolean(conf.get("oozie.https.enabled"));
LOG.info("Server started with oozie.https.enabled = " + isSSLEnabled);
return isSSLEnabled;
}
public static void setContextPath(Configuration oozieConfiguration) {
String baseUrl = oozieConfiguration.get("oozie.base.url").replaceAll("[\r\n]","");
String contextPath = baseUrl.substring(baseUrl.lastIndexOf("/"));
LOG.info("Server started with contextPath = " + contextPath);
EmbeddedOozieServer.contextPath = contextPath;
}
public static String getContextPath(Configuration oozieConfiguration) {
if (contextPath != null) {
return contextPath;
}
setContextPath(oozieConfiguration);
return EmbeddedOozieServer.contextPath;
}
public void start() throws Exception {
server.start();
LOG.info("Server started.");
}
public void shutdown() throws Exception {
LOG.info("Shutting down.");
if (serviceController != null) {
serviceController.destroy();
LOG.info("Oozie services stopped.");
}
if (server != null) {
server.stop();
LOG.info("Server stopped.");
}
}
public void join() throws InterruptedException {
server.join();
}
public void addShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
shutdown();
} catch (final Exception e) {
LOG.error("There were errors during shutdown.", e);
}
}
});
}
public static void main(String[] args) throws Exception {
final Injector guiceInjector = Guice.createInjector(new OozieGuiceModule());
EmbeddedOozieServer embeddedOozieServer = null;
try {
embeddedOozieServer = guiceInjector.getInstance(EmbeddedOozieServer.class);
} catch (final ProvisionException ex) {
LOG.error("Failed to get EmbeddedOozieServer", ex);
System.exit(1);
}
embeddedOozieServer.addShutdownHook();
embeddedOozieServer.setup();
try {
embeddedOozieServer.start();
} catch (final Exception e) {
LOG.error("Could not start EmbeddedOozieServer!", e);
System.exit(1);
}
embeddedOozieServer.join();
}
}