blob: a5eb5cca6120f75589b28abd757a268de45caa88 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2021 the Eclipse BaSyx Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipse.basyx.vab.protocol.http.server;
import java.io.File;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.UUID;
import javax.servlet.http.HttpServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Starter Class for Apache Tomcat 8.0.53 HTTP server that adds the provided servlets and respective mappings on startup.
*
* @author pschorn, espen, haque
*
*/
public class BaSyxHTTPServer {
private static Logger logger = LoggerFactory.getLogger(BaSyxHTTPServer.class);
private Tomcat tomcat;
static {
// Enable coding of forward slash in tomcat
System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
}
/**
* Constructor
*
* Create new Tomcat instance and add the provided servlet mappings
*
* @param context
* Basyx context with of url mappings to HTTPServlet
*/
public BaSyxHTTPServer(BaSyxContext context) {
// Instantiate and setup Tomcat server
tomcat = new Tomcat();
// Set random name to prevent lifecycle expections during shutdown of multiple
// instances
tomcat.getEngine().setName(UUID.randomUUID().toString());
if (context.isSecuredConnectionEnabled()) {
Connector httpsConnector = tomcat.getConnector();
configureSslConnector(context, httpsConnector);
} else {
tomcat.setPort(context.port);
}
tomcat.setHostname(context.hostname);
tomcat.getHost().setAppBase(".");
// Create servlet context
// - Base path for resource files
File docBase = new File(context.docBasePath); // System.getProperty("java.io.tmpdir"));
// - Create context for servlets
Context rootCtx = tomcat.addContext(context.contextPath, docBase.getAbsolutePath());
// Iterate all servlets in context
Iterator<Entry<String, HttpServlet>> it = context.entrySet().iterator();
while (it.hasNext()) {
// Servlet entry
Entry<String, HttpServlet> entry = it.next();
// Servlet mapping
String mapping = entry.getKey();
HttpServlet servlet = entry.getValue();
// Add new Servlet and Mapping to tomcat environment
Tomcat.addServlet(rootCtx, Integer.toString(servlet.hashCode()), servlet);
rootCtx.addServletMappingDecoded(mapping, Integer.toString(servlet.hashCode()));
}
}
/**
* SSL Configuration for SSL connector
* @param context
* @param httpsConnector
*/
private void configureSslConnector(BaSyxContext context, Connector httpsConnector) {
httpsConnector.setPort(context.port);
httpsConnector.setSecure(true);
httpsConnector.setScheme("https");
httpsConnector.setAttribute("keystoreFile", context.getCertificatePath());
httpsConnector.setAttribute("clientAuth", "false");
httpsConnector.setAttribute("sslProtocol", "TLS");
httpsConnector.setAttribute("SSLEnabled", true);
httpsConnector.setAttribute("protocol", "HTTP/1.1");
httpsConnector.setAttribute("keystorePass", context.getKeyPassword());
httpsConnector.setAttribute("keyAlias", "tomcat");
httpsConnector.setAttribute("maxThreads", "200");
httpsConnector.setAttribute("protocol", "org.apache.coyote.http11.Http11AprProtocol");
}
/**
* Starts the server in a new thread to avoid blocking the main thread
*/
public void start() {
logger.trace("Starting Tomcat.....");
Thread serverThread = new Thread(() -> {
try {
tomcat.stop();
// Adds listener that notifies the tomcat object when the server has started
tomcat.getServer().addLifecycleListener(new LifecycleListener() {
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (event.getLifecycle().getState() == LifecycleState.STARTED) {
synchronized (tomcat) {
tomcat.notifyAll();
}
}
}
});
tomcat.start();
// Keeps the server thread alive until the server is shut down
tomcat.getServer().await();
} catch (LifecycleException e) {
logger.error("Exception in start", e);
}
});
serverThread.start();
synchronized (tomcat) {
try {
tomcat.wait();
} catch (InterruptedException e) {
logger.error("Exception in start", e);
}
}
}
/**
* This Method stops and destroys the tomcat instance. This is important since Tomcat would be already
* bound to port 8080 when new tests are run that require a start of tomcat
*/
public void shutdown() {
logger.trace("Shutting down BaSyx HTTP Server...");
try {
tomcat.stop();
tomcat.destroy();
} catch (LifecycleException e) {
// TODO Auto-generated catch block
logger.error("Exception in shutdown", e);
}
}
}