com.cisco.oss.foundation.http.server.jetty.JettyHttpServerFactory Maven / Gradle / Ivy
/*
* Copyright 2014 Cisco Systems, Inc.
*
* Licensed 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 com.cisco.oss.foundation.http.server.jetty;
import com.cisco.oss.foundation.configuration.ConfigurationFactory;
import com.cisco.oss.foundation.directory.RegistrationManager;
import com.cisco.oss.foundation.directory.ServiceDirectory;
import com.cisco.oss.foundation.directory.entity.OperationalStatus;
import com.cisco.oss.foundation.directory.entity.ProvidedServiceInstance;
import com.cisco.oss.foundation.directory.exception.ServiceException;
import com.cisco.oss.foundation.directory.impl.DirectoryServiceClient;
import com.cisco.oss.foundation.http.server.HttpServerFactory;
import com.cisco.oss.foundation.http.server.ServerFailedToStartException;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.nio.BlockingChannelConnector;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* This is a factory that enables creating servers in a common way
*
* @author Yair Ogen
*/
public enum JettyHttpServerFactory implements HttpServerFactory , JettyHttpServerFactoryExtensions {
INSTANCE;
private final static Logger LOGGER = LoggerFactory.getLogger(JettyHttpServerFactory.class);
static{
try {
Configuration configuration = ConfigurationFactory.getConfiguration();
String sdHost = configuration.getString("service.sd.host", "");
int sdPort = configuration.getInt("service.sd.port", -1);
if(StringUtils.isNotBlank(sdHost)){
ServiceDirectory.getServiceDirectoryConfig().setProperty( DirectoryServiceClient.SD_API_SD_SERVER_FQDN_PROPERTY, sdHost);
}
if(sdPort > 0){
ServiceDirectory.getServiceDirectoryConfig().setProperty( DirectoryServiceClient.SD_API_SD_SERVER_PORT_PROPERTY, sdPort);
}
} catch (Exception e) {
LOGGER.error("Can't assign service Directory host and port properties: {}",e ,e);
}
}
private static final Map> servers = new ConcurrentHashMap>();
private JettyHttpServerFactory() {
}
/**
* start a new http server
*
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets) {
ArrayListMultimap filterMap = ArrayListMultimap.create();
startHttpServer(serviceName, servlets, filterMap);
}
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, Map initParams) {
ArrayListMultimap filterMap = ArrayListMultimap.create();
startHttpServer(serviceName, servlets, filterMap, Collections.emptyList(), initParams);
}
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filterMap, List eventListeners, Map initParams) {
startHttpServer(serviceName, servlets, filterMap, eventListeners, initParams, "", "", "", "");
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param eventListeners event listeners to be applied on this server
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, List eventListeners) {
ArrayListMultimap filterMap = ArrayListMultimap.create();
startHttpServer(serviceName, servlets, filterMap, eventListeners);
}
/**
* start a new http server
*
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param filters - a mapping between filter path and filter instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap filterMap = ArrayListMultimap.create()}
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters) {
startHttpServer(serviceName, servlets, filters, "", "");
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param filters - a mapping between filter path and filter instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap filterMap = ArrayListMultimap.create()}
* @param eventListeners event listeners to be applied on this server
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters, List eventListeners) {
startHttpServer(serviceName, servlets, filters, eventListeners, "", "", "", "");
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param filters - a mapping between filter path and filter instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap filterMap = ArrayListMultimap.create()}
* @param keyStorePath - a path to the keystore file
* @param keyStorePassword - the keystore password
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters, String keyStorePath, String keyStorePassword) {
startHttpServer(serviceName, servlets, filters, keyStorePath, keyStorePassword, "", "");
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param keyStorePath - a path to the keystore file
* @param keyStorePassword - the keystore password
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, String keyStorePath, String keyStorePassword) {
startHttpServer(serviceName, servlets, keyStorePath, keyStorePassword, "", "");
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param filters - a mapping between filter path and filter instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap filterMap = ArrayListMultimap.create()}
* @param keyStorePath - a path to the keystore file
* @param keyStorePassword - the keystore password
* @param trustStorePath - the trust store file path
* @param trustStorePassword - the trust store password
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters, String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword) {
startHttpServer(serviceName, servlets, filters, Collections.emptyList(), keyStorePath, keyStorePassword, trustStorePath, trustStorePassword);
}
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters, List eventListeners, String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword) {
startHttpServer(serviceName, servlets, filters, eventListeners, new HashMap(), keyStorePath, keyStorePassword, trustStorePath, trustStorePassword);
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param filters - a mapping between filter path and filter instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap filterMap = ArrayListMultimap.create()}
* @param eventListeners event listeners to be applied on this server
* @param keyStorePath - a path to the keystore file
* @param keyStorePassword - the keystore password
* @param trustStorePath - the trust store file path
* @param trustStorePassword - the trust store password
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, ListMultimap filters, List eventListeners, Map initParams, String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword) {
if (servers.get(serviceName) != null) {
throw new UnsupportedOperationException("you must first stop stop server: " + serviceName + " before you want to start it again!");
}
ContextHandlerCollection handler = new ContextHandlerCollection();
JettyHttpThreadPool jettyHttpThreadPool = new JettyHttpThreadPool(serviceName);
for (Map.Entry entry : servlets.entries()) {
ServletContextHandler context = new ServletContextHandler();
if (eventListeners != null && !eventListeners.isEmpty()) {
context.setEventListeners(eventListeners.toArray(new EventListener[0]));
}
context.addServlet(new ServletHolder(entry.getValue()), entry.getKey());
for (Map.Entry initParam : initParams.entrySet()) {
context.setInitParameter(initParam.getKey(), initParam.getValue());
}
HttpServerUtil.addFiltersToServletContextHandler(serviceName, jettyHttpThreadPool, context);
for (Map.Entry filterEntry : filters.entries()) {
context.addFilter(new FilterHolder(filterEntry.getValue()), filterEntry.getKey(), EnumSet.allOf(DispatcherType.class));
}
handler.addHandler(context);
}
Server server = new Server();
server.setSendServerVersion(false);
try {
Configuration configuration = ConfigurationFactory.getConfiguration();
// set connectors
String host = configuration.getString(serviceName + ".http.host", "0.0.0.0");
int port = configuration.getInt(serviceName + ".http.port", 8080);
int connectionIdleTime = configuration.getInt(serviceName + ".http.connectionIdleTime", 180000);
boolean isBlockingChannelConnector = configuration.getBoolean(serviceName + ".http.isBlockingChannelConnector", false);
int numberOfAcceptors = configuration.getInt(serviceName + ".http.numberOfAcceptors", Runtime.getRuntime().availableProcessors());
int acceptQueueSize = configuration.getInt(serviceName + ".http.acceptQueueSize", 0);
boolean serviceDirectoryEnabled = configuration.getBoolean(serviceName + ".http.serviceDirectory.isEnabled", false);
AbstractConnector connector = null;
if (isBlockingChannelConnector) {
connector = new BlockingChannelConnector();
} else {
connector = new SelectChannelConnector();
}
connector.setAcceptQueueSize(acceptQueueSize);
connector.setAcceptors(numberOfAcceptors);
connector.setPort(port);
connector.setHost(host);
connector.setMaxIdleTime(connectionIdleTime);
connector.setRequestHeaderSize(configuration.getInt(serviceName + ".http.requestHeaderSize", connector.getRequestHeaderSize()));
Connector[] connectors = null;
boolean useHttpsOnly = configuration.getBoolean(serviceName + ".https.useHttpsOnly", false);
boolean isSSL = StringUtils.isNotBlank(keyStorePath) && StringUtils.isNotBlank(keyStorePassword);
int sslPort = -1;
SslSelectChannelConnector sslSelectChannelConnector = null;
if (isSSL) {
String sslHost = configuration.getString(serviceName + ".https.host", "0.0.0.0");
sslPort = configuration.getInt(serviceName + ".https.port", 8090);
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword(keyStorePassword);
boolean addTrustStoreSupport = StringUtils.isNotEmpty(trustStorePath) && StringUtils.isNotEmpty(trustStorePassword);
if (addTrustStoreSupport) {
sslContextFactory.setTrustStore(trustStorePath);
sslContextFactory.setTrustStorePassword(trustStorePassword);
sslContextFactory.setNeedClientAuth(true);
}
sslSelectChannelConnector = new SslSelectChannelConnector(sslContextFactory);
sslSelectChannelConnector.setHost(sslHost);
sslSelectChannelConnector.setPort(sslPort);
if (useHttpsOnly) {
connectors = new Connector[]{sslSelectChannelConnector};
} else {
connectors = new Connector[]{connector, sslSelectChannelConnector};
}
} else {
connectors = new Connector[]{connector};
}
server.setConnectors(connectors);
// set thread pool
server.setThreadPool(jettyHttpThreadPool.threadPool);
// set servlets/context handlers
server.setHandler(handler);
server.start();
ProvidedServiceInstance instance = null;
if (serviceDirectoryEnabled) {
instance = registerWithSDServer(serviceName, host, port);
}
servers.put(serviceName, Pair.of(server,instance));
if (sslPort != -1 && sslSelectChannelConnector != null) {
LOGGER.info("Https server: {} started on {}", serviceName, sslPort);
}
if(!useHttpsOnly){
LOGGER.info("Http server: {} started on {}", serviceName, port);
}
// server.join();
} catch (Exception e) {
LOGGER.error("Problem starting the http {} server. Error is {}.", new Object[]{serviceName, e, e});
throw new ServerFailedToStartException(e);
}
}
/**
* @param serviceName - the http logical service name
* @param servlets - a mapping between servlet path and servlet instance. This mapping uses the google collections {@link com.google.common.collect.ListMultimap}.
*
Example of usage:
* {@code ArrayListMultimap servletMap = ArrayListMultimap.create()}
* @param keyStorePath - a path to the keystore file
* @param keyStorePassword - the keystore password
* @param trustStorePath - the trust store file path
* @param trustStorePassword - the trust store password
*/
@Override
public void startHttpServer(String serviceName, ListMultimap servlets, String keyStorePath, String keyStorePassword, String trustStorePath, String trustStorePassword) {
ArrayListMultimap filterMap = ArrayListMultimap.create();
startHttpServer(serviceName, servlets, filterMap, keyStorePath, keyStorePassword, trustStorePath, trustStorePassword);
}
/**
* stop the http server
*
* @param serviceName - the http logical service name
*/
@Override
public void stopHttpServer(String serviceName) {
Pair pair = servers.get(serviceName);
if (pair != null) {
Server server = pair.getLeft();
if (server != null) {
try {
server.stop();
servers.remove(serviceName);
LOGGER.info("Http server: {} stopped", serviceName);
} catch (Exception e) {
LOGGER.error("Problem stopping the http {} server. Error is {}.", serviceName, e);
}
}
ProvidedServiceInstance instance = pair.getRight();
if(instance != null){
try {
final RegistrationManager registrationManager = ServiceDirectory.getRegistrationManager();
// registrationManager.updateServiceOperationalStatus(instance.getServiceName(),instance.getProviderId(),OperationalStatus.DOWN);
registrationManager.unregisterService(instance.getServiceName(),instance.getProviderId());
} catch (ServiceException e) {
LOGGER.error("Problem stopping the http {} server. Error is {}.", serviceName, e);
}
}
}
}
@Override
public void setErrorHandler(String serviceName, ErrorHandler errorHandler) {
Server server = servers.get(serviceName).getLeft();
if (server != null) {
server.addBean(errorHandler);
}
}
private ProvidedServiceInstance registerWithSDServer(final String serviceName, String host, int port) throws Exception {
// Get a RegistrationManager instance from ServiceDirectory.
// The ServiceDirectory will load a default ServiceDirectoryConfig and instantialize a RegistrationManager instance.
final RegistrationManager registrationManager = ServiceDirectory.getRegistrationManager();
// Construct the service instance. serviceName and providerId together uniquely identify a service instance where providerId is defined as "address-port".
final ProvidedServiceInstance instance = new ProvidedServiceInstance(serviceName, host, port);
// Setting the service instance URI. URI is defined as tcp://address:port for the TCP end point
instance.setUri("http://" + host + ":" + port + "");
instance.setAddress(host);
instance.setPort(port);
instance.setStatus(OperationalStatus.UP);
registrationManager.registerService(instance, null);
// Update the OperationalStatus of the instance to DOWN, this instance will be removed from the ServiceInstance lookup.
// registrationManager.updateServiceOperationalStatus(serviceName, instance.getProviderId(), OperationalStatus.DOWN);
// Update the instance: OperationalStatus to UP, and the "version" metadata to "2.5.1"
// instance.setStatus(OperationalStatus.UP);
// instance.getMetadata().put("version", "2.5.1");
// instance.getMetadata().put("datacenter", "dc01");
// registrationManager.updateService(instance);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
// Unregister the instance.
try {
registrationManager.unregisterService(serviceName, instance.getProviderId());
} catch (ServiceException e) {
LOGGER.error("can't un-register the service in service directory server. error is: {}", e);
}
}
});
return instance;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy