blob: 9a913cb1896dff4389f0a175bd3bc061c84598b8 [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.lens.server;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Logger;
import javax.ws.rs.core.UriBuilder;
import org.apache.lens.api.jaxb.LensJAXBContextResolver;
import org.apache.lens.server.api.LensConfConstants;
import org.apache.lens.server.api.authorization.LensAuthorizer;
import org.apache.lens.server.api.metrics.MetricsService;
import org.apache.lens.server.error.GenericExceptionMapper;
import org.apache.lens.server.error.LensJAXBValidationExceptionMapper;
import org.apache.lens.server.metrics.MetricsServiceImpl;
import org.apache.lens.server.model.MappedDiagnosticLogSegregationContext;
import org.apache.hadoop.hive.conf.HiveConf;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.http.server.NetworkListener;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.grizzly.ssl.SSLContextConfigurator;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.process.JerseyProcessingUncaughtExceptionHandler;
import org.glassfish.jersey.server.ResourceConfig;
import org.slf4j.bridge.SLF4JBridgeHandler;
import com.codahale.metrics.servlets.AdminServlet;
import jersey.repackaged.com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
* The Class LensServer.
*/
@Slf4j
public class LensServer {
private static final String SEP_LINE =
"\n###############################################################\n";
@Getter
private final List<HttpServer> serverList = new ArrayList<HttpServer>();
/**
* This flag indicates that the lens server can run, When this is set to false, main thread bails out.
*/
volatile boolean canRun = true;
static {
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
}
static LensServer createLensServer(HiveConf conf) throws IOException {
final LensServer thisServer = new LensServer(conf);
return thisServer;
}
/**
* Instantiates a new lens server.
*
* @param conf the conf
* @throws IOException Signals that an I/O exception has occurred.
*/
private LensServer(HiveConf conf) throws IOException {
startServices(conf);
HttpServer server = getHttpServer(conf);
int corePoolSize = conf.getInt(LensConfConstants.GRIZZLY_CORE_POOL_SIZE,
LensConfConstants.DEFAULT_GRIZZLY_CORE_POOL_SIZE);
int maxPoolSize = conf.getInt(LensConfConstants.GRIZZLY_MAX_POOL_SIZE,
LensConfConstants.DEFAULT_GRIZZLY_MAX_POOL_SIZE);
ThreadPoolConfig config = ThreadPoolConfig.defaultConfig();
config.setPoolName("lensserver-pool");
config.setCorePoolSize(corePoolSize);
config.setMaxPoolSize(maxPoolSize);
config.setThreadFactory((new ThreadFactoryBuilder()).setNameFormat("grizzly-http-server-%d")
.setUncaughtExceptionHandler(new JerseyProcessingUncaughtExceptionHandler()).build());
NetworkListener listener = server.getListeners().iterator().next();
listener.getTransport().setWorkerThreadPoolConfig(config);
serverList.add(server);
WebappContext adminCtx = new WebappContext("admin", "");
MetricsServiceImpl metricsService = LensServices.get().getService(MetricsService.NAME);
adminCtx
.setAttribute("com.codahale.metrics.servlets.MetricsServlet.registry", (metricsService.getMetricRegistry()));
adminCtx.setAttribute("com.codahale.metrics.servlets.HealthCheckServlet.registry", metricsService.getHealthCheck());
final ServletRegistration sgMetrics = adminCtx.addServlet("admin", new AdminServlet());
sgMetrics.addMapping("/admin/*");
adminCtx.deploy(server);
}
private ResourceConfig getApp() {
ResourceConfig app = ResourceConfig.forApplicationClass(LensApplication.class);
app.register(new LoggingFilter(Logger.getLogger(LensServer.class.getName() + ".request"), true));
app.register(GenericExceptionMapper.class);
app.register(LensJAXBValidationExceptionMapper.class);
app.register(LensJAXBContextResolver.class);
app.setApplicationName("AllApps");
return app;
}
/**
* Start services.
*
* @param conf the conf
*/
public void startServices(HiveConf conf) {
LensAuthorizer.get().init(conf);
LensServices.get().init(conf);
LensServices.get().start();
}
/**
* Start.
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public synchronized void start() throws IOException {
for (HttpServer server : serverList) {
server.start();
}
}
/**
* Stop.
*/
public synchronized void stop() {
for (HttpServer server : serverList) {
server.shutdown();
}
LensServices.get().stop();
printShutdownMessage();
}
/**
* This keeps the server running till a shutdown is triggered. Either through a shutdown sequence initiated by an
* administrator or if applications encounters a fatal exception or it enters an unrecoverable state.
*/
private void join() {
while (canRun) {
synchronized (this) {
try {
wait(2000);
} catch (InterruptedException e) {
log.warn("Received an interrupt in the main loop", e);
}
}
}
log.info("Exiting main run loop...");
}
/**
* The main method.
*
* @param args the arguments
* @throws Exception the exception
*/
public static void main(String[] args) throws Exception {
final String runId = UUID.randomUUID().toString();
new MappedDiagnosticLogSegregationContext().setLogSegregationId(runId);
printStartupMessage();
try {
final LensServer thisServer = LensServer.createLensServer(LensServerConf.getHiveConf());
registerShutdownHook(thisServer);
registerDefaultExceptionHandler();
thisServer.start();
thisServer.join();
} catch (Exception exc) {
log.error("Error while creating Lens server", exc);
try {
LensServices.get().stop();
} catch (Exception e) {
log.error("Error stopping services", e);
}
}
}
/**
* Print message from lens-build-info file during startup.
*/
private static void printStartupMessage() {
StringBuilder buffer = new StringBuilder();
buffer.append(SEP_LINE);
buffer.append(" Lens Server (STARTUP)");
Properties buildProperties = new Properties();
InputStream buildPropertiesResource = LensServer.class.
getResourceAsStream("/lens-build-info.properties");
if (buildPropertiesResource != null) {
try {
buildProperties.load(buildPropertiesResource);
for (Map.Entry entry : buildProperties.entrySet()) {
buffer.append('\n').append('\t').append(entry.getKey()).
append(":\t").append(entry.getValue());
}
} catch (Throwable e) {
buffer.append("*** Unable to get build info ***");
}
} else {
buffer.append("*** Unable to get build info ***");
}
buffer.append(SEP_LINE);
log.info(buffer.toString());
}
/**
* Print message before the lens server stops.
*/
private static void printShutdownMessage() {
StringBuilder buffer = new StringBuilder();
buffer.append(SEP_LINE);
buffer.append(" Lens Server (SHUTDOWN)");
buffer.append(SEP_LINE);
log.info(buffer.toString());
}
/**
* Registering a shutdown hook to listen to SIGTERM events. Upon receiving a SIGTERM, notify the server, which is put
* on wait state.
*/
private static void registerShutdownHook(final LensServer thisServer) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
Thread.currentThread().setName("Shutdown");
log.info("Server has been requested to be stopped.");
thisServer.canRun = false;
thisServer.stop();
}
});
}
/** Registering a default uncaught exception handler. */
private static void registerDefaultExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Uncaught exception in Thread " + t, e);
}
});
}
private HttpServer getHttpServer(HiveConf conf) throws IOException {
String baseURI = conf.get(LensConfConstants.SERVER_BASE_URL, LensConfConstants.DEFAULT_SERVER_BASE_URL);
if (Boolean.valueOf(conf.get(LensConfConstants.SSL_ENABLED,
String.valueOf(LensConfConstants.DEFAULT_SSL_ENABLED_VALUE)))) {
log.info("SSL is enabled, starting lens server in https mode.");
SSLContextConfigurator sslCon = new SSLContextConfigurator();
String keyStoreFile = conf.get(LensConfConstants.SSL_KEYSTORE_FILE_PATH);
String sslPassword = conf.get(LensConfConstants.SSL_KEYSTORE_PASSWORD);
if (keyStoreFile == null || keyStoreFile.isEmpty()) {
throw new IOException(
String.format("SSL is enabled but cert file is missing, "
+ "%s should be initialize with valid cert file path.",
LensConfConstants.SSL_KEYSTORE_FILE_PATH));
}
sslCon.setKeyStoreFile(keyStoreFile);
sslCon.setKeyStorePass(sslPassword);
return GrizzlyHttpServerFactory.createHttpServer(UriBuilder.fromUri(baseURI).build(), getApp(),
true, new SSLEngineConfigurator(sslCon).setClientMode(false).setNeedClientAuth(false));
} else {
log.info("SSL is not enabled, starting lens server in http mode.");
return GrizzlyHttpServerFactory.createHttpServer(UriBuilder.fromUri(baseURI).build(), getApp(), false);
}
}
}