+package org.argeo.cms.jetty;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+import javax.websocket.server.ServerEndpointConfig;
+
+import org.argeo.api.cms.CmsLog;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.CmsDeployProperty;
+import org.argeo.cms.websocket.javax.server.CmsWebSocketConfigurator;
+import org.argeo.cms.websocket.javax.server.TestEndpoint;
+import org.eclipse.jetty.http.UriCompliance;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+
+public class CmsJettyServer {
+ private static final CmsLog log = CmsLog.getLog(CmsJettyServer.class);
+
+ private static final int DEFAULT_IDLE_TIMEOUT = 30000;
+ private static final String CONTEXT_TEMPDIR = "javax.servlet.context.tempdir";
+
+ // Equinox compatibility
+ private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
+
+ private Server server;
+ private Path tempDir;
+
+ private ServerConnector httpConnector;
+ private ServerConnector httpsConnector;
+
+ // WebSocket
+ private ServerContainer wsServerContainer;
+ private ServerEndpointConfig.Configurator wsEndpointConfigurator;
+
+ private CmsState cmsState;
+
+ public void start() {
+ try {
+ tempDir = Files.createTempDirectory("jetty");
+
+ server = new Server(new QueuedThreadPool(10, 1));
+
+ configure();
+ // context.addServlet(new ServletHolder(new RWTServlet()), "/" + entryPoint);
+ // Required to serve rwt-resources. It is important that this is last.
+// ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
+// context.addServlet(holderPwd, "/");
+
+ if (httpConnector != null) {
+ httpConnector.open();
+ server.addConnector(httpConnector);
+ }
+
+ if (httpsConnector != null) {
+ httpsConnector.open();
+ server.addConnector(httpsConnector);
+ }
+
+ // holder
+
+ // context
+ ServletContextHandler httpContext = createHttpContext();
+ // httpContext.addServlet(holder, "/*");
+ addServlets(httpContext);
+ enableWebSocket(httpContext);
+ server.setHandler(httpContext);
+
+ //
+ // START
+ server.start();
+ //
+
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
+
+ log.info(httpPortsMsg());
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot start Jetty HTTPS server", e);
+ }
+ }
+
+ protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
+ }
+
+ public Integer getHttpPort() {
+ if (httpConnector == null)
+ return null;
+ return httpConnector.getLocalPort();
+ }
+
+ public Integer getHttpsPort() {
+ if (httpsConnector == null)
+ return null;
+ return httpsConnector.getLocalPort();
+ }
+
+ public void stop() {
+ try {
+ // serverConnector.close();
+ server.stop();
+ // TODO delete temp dir
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ protected void configure() {
+ HttpConfiguration http_config = new HttpConfiguration();
+
+ String httpPortStr = getFrameworkProp(CmsDeployProperty.HTTP_PORT);
+ String httpsPortStr = getFrameworkProp(CmsDeployProperty.HTTPS_PORT);
+
+ /// TODO make it more generic
+ String httpHost = getFrameworkProp(CmsDeployProperty.HOST);
+// String httpsHost = getFrameworkProp(
+// JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
+
+ // try {
+ if (httpPortStr != null || httpsPortStr != null) {
+ boolean httpEnabled = httpPortStr != null;
+ // props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
+ boolean httpsEnabled = httpsPortStr != null;
+ // props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
+ if (httpsEnabled) {
+ int httpsPort = Integer.parseInt(httpsPortStr);
+ http_config.setSecureScheme("https");
+ http_config.setSecurePort(httpsPort);
+ }
+
+ if (httpEnabled) {
+ int httpPort = Integer.parseInt(httpPortStr);
+ httpConnector = new ServerConnector(server, new HttpConnectionFactory(http_config));
+ httpConnector.setPort(httpPort);
+ httpConnector.setHost(httpHost);
+ httpConnector.setIdleTimeout(DEFAULT_IDLE_TIMEOUT);
+ }
+
+ if (httpsEnabled) {
+
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ // sslContextFactory.setKeyStore(KeyS)
+
+ sslContextFactory.setKeyStoreType(getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
+ sslContextFactory.setKeyStorePath(getFrameworkProp(CmsDeployProperty.SSL_KEYSTORE));
+ sslContextFactory.setKeyStorePassword(getFrameworkProp(CmsDeployProperty.SSL_PASSWORD));
+ // sslContextFactory.setKeyManagerPassword(getFrameworkProp(CmsDeployProperty.SSL_KEYPASSWORD));
+ sslContextFactory.setProtocol("TLS");
+
+ sslContextFactory.setTrustStoreType(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORETYPE));
+ sslContextFactory.setTrustStorePath(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORE));
+ sslContextFactory.setTrustStorePassword(getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
+
+ String wantClientAuth = getFrameworkProp(CmsDeployProperty.SSL_WANTCLIENTAUTH);
+ if (wantClientAuth != null && wantClientAuth.equals(Boolean.toString(true)))
+ sslContextFactory.setWantClientAuth(true);
+ String needClientAuth = getFrameworkProp(CmsDeployProperty.SSL_NEEDCLIENTAUTH);
+ if (needClientAuth != null && needClientAuth.equals(Boolean.toString(true)))
+ sslContextFactory.setNeedClientAuth(true);
+
+ // HTTPS Configuration
+ HttpConfiguration https_config = new HttpConfiguration(http_config);
+ https_config.addCustomizer(new SecureRequestCustomizer());
+ https_config.setUriCompliance(UriCompliance.LEGACY);
+
+ // HTTPS connector
+ httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"),
+ new HttpConnectionFactory(https_config));
+ int httpsPort = Integer.parseInt(httpsPortStr);
+ httpsConnector.setPort(httpsPort);
+ httpsConnector.setHost(httpHost);
+ }
+
+ }
+
+ }
+
+ protected void enableWebSocket(ServletContextHandler servletContextHandler) {
+ String webSocketEnabled = getFrameworkProp(CmsDeployProperty.WEBSOCKET_ENABLED);
+ // web socket
+ if (webSocketEnabled != null && webSocketEnabled.equals(Boolean.toString(true))) {
+ JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
+
+ @Override
+ public void accept(ServletContext servletContext, ServerContainer serverContainer)
+ throws DeploymentException {
+ wsServerContainer = serverContainer;
+
+ wsEndpointConfigurator = new CmsWebSocketConfigurator();
+
+ ServerEndpointConfig config = ServerEndpointConfig.Builder
+ .create(TestEndpoint.class, "/ws/test/events/").configurator(wsEndpointConfigurator)
+ .build();
+ try {
+ wsServerContainer.addEndpoint(config);
+ } catch (DeploymentException e) {
+ throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
+ }
+ }
+ });
+ }
+ }
+
+ protected ServletContextHandler createHttpContext() {
+ ServletContextHandler httpContext = new ServletContextHandler();
+ httpContext.setAttribute(INTERNAL_CONTEXT_CLASSLOADER, Thread.currentThread().getContextClassLoader());
+ httpContext.setClassLoader(this.getClass().getClassLoader());
+ httpContext.setContextPath("/");
+
+ httpContext.setAttribute(CONTEXT_TEMPDIR, tempDir.toAbsolutePath().toFile());
+ SessionHandler handler = new SessionHandler();
+ handler.setMaxInactiveInterval(-1);
+ httpContext.setSessionHandler(handler);
+
+ return httpContext;
+ }
+
+ private String httpPortsMsg() {
+
+ return (httpConnector != null ? "HTTP " + getHttpPort() + " " : " ")
+ + (httpsConnector != null ? "HTTPS " + getHttpsPort() : "");
+ }
+
+ private String getFrameworkProp(CmsDeployProperty deployProperty) {
+ return cmsState.getDeployProperty(deployProperty.getProperty());
+ }
+
+ public void setCmsState(CmsState cmsState) {
+ this.cmsState = cmsState;
+ }
+
+}