blob: 7cc0129873d46d615a10f6db2083de1c06b069c4 [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.storm.daemon.ui;
import static org.apache.storm.utils.ConfigUtils.FILE_SEPARATOR;
import static org.apache.storm.utils.ConfigUtils.STORM_HOME;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.apache.storm.DaemonConfig;
import org.apache.storm.daemon.drpc.webapp.ReqContextFilter;
import org.apache.storm.daemon.ui.exceptionmappers.AuthorizationExceptionMapper;
import org.apache.storm.daemon.ui.exceptionmappers.DefaultExceptionMapper;
import org.apache.storm.daemon.ui.exceptionmappers.NotAliveExceptionMapper;
import org.apache.storm.daemon.ui.filters.AuthorizedUserFilter;
import org.apache.storm.daemon.ui.filters.HeaderResponseFilter;
import org.apache.storm.daemon.ui.filters.HeaderResponseServletFilter;
import org.apache.storm.metric.StormMetricsRegistry;
import org.apache.storm.security.auth.IHttpCredentialsPlugin;
import org.apache.storm.security.auth.ServerAuthUtils;
import org.apache.storm.utils.ConfigUtils;
import org.apache.storm.utils.ObjectReader;
import org.apache.storm.utils.Utils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Main class.
*
*/
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
public class UIServer {
public static final Logger LOG = LoggerFactory.getLogger(UIServer.class);
public static final String STORM_API_URL_PREFIX = "/api/v1/";
/**
* addRequestContextFilter.
* @param context context
* @param configName configName
* @param conf conf
*/
public static void addRequestContextFilter(ServletContextHandler context,
String configName, Map<String, Object> conf) {
IHttpCredentialsPlugin auth = ServerAuthUtils.getHttpCredentialsPlugin(conf, (String) conf.get(configName));
ReqContextFilter filter = new ReqContextFilter(auth);
context.addFilter(new FilterHolder(filter), "/*", EnumSet.allOf(DispatcherType.class));
}
/**
* main.
* @param args args
*/
public static void main(String[] args) {
Map<String, Object> conf = ConfigUtils.readStormConfig();
int headerBufferSize = (int) conf.get(DaemonConfig.UI_HEADER_BUFFER_BYTES);
final Integer httpsPort = ObjectReader.getInt(conf.get(DaemonConfig.UI_HTTPS_PORT), 0);
final String httpsKsPath = (String) (conf.get(DaemonConfig.UI_HTTPS_KEYSTORE_PATH));
final String httpsKsPassword = (String) (conf.get(DaemonConfig.UI_HTTPS_KEYSTORE_PASSWORD));
final String httpsKsType = (String) (conf.get(DaemonConfig.UI_HTTPS_KEYSTORE_TYPE));
final String httpsKeyPassword = (String) (conf.get(DaemonConfig.UI_HTTPS_KEY_PASSWORD));
final String httpsTsPath = (String) (conf.get(DaemonConfig.UI_HTTPS_TRUSTSTORE_PATH));
final String httpsTsPassword = (String) (conf.get(DaemonConfig.UI_HTTPS_TRUSTSTORE_PASSWORD));
final String httpsTsType = (String) (conf.get(DaemonConfig.UI_HTTPS_TRUSTSTORE_TYPE));
final Boolean httpsWantClientAuth = (Boolean) (conf.get(DaemonConfig.UI_HTTPS_WANT_CLIENT_AUTH));
final Boolean httpsNeedClientAuth = (Boolean) (conf.get(DaemonConfig.UI_HTTPS_NEED_CLIENT_AUTH));
final Boolean disableHttpBinding = (Boolean) (conf.get(DaemonConfig.UI_DISABLE_HTTP_BINDING));
Server jettyServer =
UIHelpers.jettyCreateServer(
(int) conf.get(DaemonConfig.UI_PORT), null, httpsPort, headerBufferSize, disableHttpBinding);
UIHelpers.configSsl(jettyServer, httpsPort, httpsKsPath, httpsKsPassword, httpsKsType, httpsKeyPassword,
httpsTsPath, httpsTsPassword, httpsTsType, httpsNeedClientAuth, httpsWantClientAuth);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
jettyServer.setHandler(context);
FilterConfiguration filterConfiguration =
new FilterConfiguration(
(String) conf.get(DaemonConfig.UI_FILTER),
(Map<String, String>) conf.get(DaemonConfig.UI_FILTER_PARAMS)
);
final List<FilterConfiguration> filterConfigurationList = Arrays.asList(filterConfiguration);
UIHelpers.configFilters(context, filterConfigurationList);
StormMetricsRegistry metricsRegistry = new StormMetricsRegistry();
ResourceConfig resourceConfig =
new ResourceConfig()
.packages("org.apache.storm.daemon.ui.resources")
.registerInstances(new AbstractBinder() {
@Override
protected void configure() {
super.bind(metricsRegistry).to(StormMetricsRegistry.class);
}
})
.register(AuthorizedUserFilter.class)
.register(HeaderResponseFilter.class)
.register(AuthorizationExceptionMapper.class)
.register(NotAliveExceptionMapper.class)
.register(DefaultExceptionMapper.class);
ServletHolder jerseyServlet = new ServletHolder(new ServletContainer(resourceConfig));
jerseyServlet.setInitOrder(0);
context.addServlet(jerseyServlet, STORM_API_URL_PREFIX + "*");
addRequestContextFilter(context, DaemonConfig.UI_HTTP_CREDS_PLUGIN, conf);
// add special pathspec of static content mapped to the homePath
ServletHolder holderHome = new ServletHolder("static-home", DefaultServlet.class);
String packagedStaticFileLocation = System.getProperty(STORM_HOME) + FILE_SEPARATOR + "public/";
if (Files.exists(Paths.get(packagedStaticFileLocation))) {
holderHome.setInitParameter("resourceBase", packagedStaticFileLocation);
} else {
LOG.warn("Cannot find static file directory in " + packagedStaticFileLocation
+ " - assuming that UIServer is being launched"
+ "in a development environment and not from a packaged release");
String developmentStaticFileLocation =
UIServer.class.getProtectionDomain().getCodeSource().getLocation().getPath()
+ "WEB-INF";
if (Files.exists(Paths.get(developmentStaticFileLocation))) {
holderHome.setInitParameter("resourceBase", developmentStaticFileLocation);
} else {
throw new RuntimeException("Cannot find static file directory in development "
+ "location " + developmentStaticFileLocation);
}
}
holderHome.setInitParameter("dirAllowed","true");
holderHome.setInitParameter("pathInfoOnly","true");
context.addFilter(new FilterHolder(new HeaderResponseServletFilter(metricsRegistry)), "/*", EnumSet.allOf(DispatcherType.class));
context.addServlet(holderHome,"/*");
// Lastly, the default servlet for root content (always needed, to satisfy servlet spec)
ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
holderPwd.setInitParameter("dirAllowed","true");
context.addServlet(holderPwd,"/");
metricsRegistry.startMetricsReporters(conf);
Utils.addShutdownHookWithForceKillIn1Sec(metricsRegistry::stopMetricsReporters);
try {
jettyServer.start();
jettyServer.join();
} catch (Throwable t) {
LOG.error("Exception in UIServer: ", t);
throw new RuntimeException(t);
}
}
}