Refactor Jetty HTTP server
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 10 Jul 2022 09:29:54 +0000 (11:29 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 10 Jul 2022 09:29:54 +0000 (11:29 +0200)
16 files changed:
org.argeo.cms.lib.equinox/.project
org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml
org.argeo.cms.lib.equinox/bnd.bnd
org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java [new file with mode: 0644]
org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java
org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyServiceFactory.java [deleted file]
org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java
org.argeo.cms.lib.jetty/.settings/org.eclipse.jdt.core.prefs [deleted file]
org.argeo.cms.lib.jetty/.settings/org.eclipse.pde.core.prefs [deleted file]
org.argeo.cms.lib.jetty/bnd.bnd
org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java [new file with mode: 0644]
org.argeo.cms.lib.jetty/src/org/argeo/cms/lib/jetty/CmsJettyServer.java [deleted file]
swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/CmsRapCli.java
swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RapJettyServer.java [new file with mode: 0644]
swt/rap/org.argeo.cms.swt.rap/.project
swt/rap/org.argeo.cms.swt.rap/src/org/argeo/cms/web/CmsWebApp.java

index 3be5eb6c2150cad3b992edb11552189537c7541c..c551d5dff615f7a1fef88ac71ca9af850c57a3a9 100644 (file)
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.pde.PluginNature</nature>
index c2cf1c73bd3f0c2df77de05f89a4137ce58b3225..2eec733bc97f7f0e36f4e8c1f7e0a2397baa2fa9 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" activate="start" deactivate="stop" name="Jetty Service Factory">
-   <implementation class="org.argeo.cms.servlet.internal.jetty.JettyConfig"/>
+   <implementation class="org.argeo.cms.equinox.http.jetty.EquinoxJettyServer"/>
    <property name="service.pid" type="String" value="org.argeo.equinox.jetty.config"/>
    <reference bind="setCmsState" cardinality="1..1" interface="org.argeo.api.cms.CmsState" name="CmsState" policy="static"/>
 </scr:component>
index 3d836dfb615815fdfcd031b4ce65389a1c85c22f..2c83158e243cf69e0176afc5a7cc914c1f512806 100644 (file)
@@ -1,4 +1,2 @@
-Fragment-Host: org.eclipse.equinox.http.jetty
-
 Service-Component: \
 OSGI-INF/jettyServiceFactory.xml,\
diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java b/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java
new file mode 100644 (file)
index 0000000..d73bdfd
--- /dev/null
@@ -0,0 +1,152 @@
+package org.argeo.cms.equinox.http.jetty;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionIdListener;
+import javax.servlet.http.HttpSessionListener;
+
+import org.argeo.cms.jetty.CmsJettyServer;
+import org.eclipse.equinox.http.servlet.HttpServiceServlet;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.osgi.framework.Constants;
+
+public class EquinoxJettyServer extends CmsJettyServer {
+       private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
+
+       @Override
+       protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
+               ServletHolder holder = new ServletHolder(new InternalHttpServiceServlet());
+               holder.setInitOrder(0);
+               holder.setInitParameter(Constants.SERVICE_VENDOR, "Eclipse.org"); //$NON-NLS-1$
+               holder.setInitParameter(Constants.SERVICE_DESCRIPTION, "Equinox Jetty-based Http Service"); //$NON-NLS-1$
+
+               // holder.setInitParameter(JettyConstants.CONTEXT_PATH,
+               // httpContext.getContextPath());
+               servletContextHandler.addServlet(holder, "/*");
+
+               // post-start
+               SessionHandler sessionManager = servletContextHandler.getSessionHandler();
+               sessionManager.addEventListener((HttpSessionIdListener) holder.getServlet());
+       }
+
+       public static class InternalHttpServiceServlet implements HttpSessionListener, HttpSessionIdListener, Servlet {
+               private final Servlet httpServiceServlet = new HttpServiceServlet();
+               private ClassLoader contextLoader;
+               private final Method sessionDestroyed;
+               private final Method sessionIdChanged;
+
+               public InternalHttpServiceServlet() {
+                       Class<?> clazz = httpServiceServlet.getClass();
+
+                       try {
+                               sessionDestroyed = clazz.getMethod("sessionDestroyed", new Class<?>[] { String.class }); //$NON-NLS-1$
+                       } catch (Exception e) {
+                               throw new IllegalStateException(e);
+                       }
+                       try {
+                               sessionIdChanged = clazz.getMethod("sessionIdChanged", new Class<?>[] { String.class }); //$NON-NLS-1$
+                       } catch (Exception e) {
+                               throw new IllegalStateException(e);
+                       }
+               }
+
+               @Override
+               public void init(ServletConfig config) throws ServletException {
+                       ServletContext context = config.getServletContext();
+                       contextLoader = (ClassLoader) context.getAttribute(INTERNAL_CONTEXT_CLASSLOADER);
+
+                       Thread thread = Thread.currentThread();
+                       ClassLoader current = thread.getContextClassLoader();
+                       thread.setContextClassLoader(contextLoader);
+                       try {
+                               httpServiceServlet.init(config);
+                       } finally {
+                               thread.setContextClassLoader(current);
+                       }
+               }
+
+               @Override
+               public void destroy() {
+                       Thread thread = Thread.currentThread();
+                       ClassLoader current = thread.getContextClassLoader();
+                       thread.setContextClassLoader(contextLoader);
+                       try {
+                               httpServiceServlet.destroy();
+                       } finally {
+                               thread.setContextClassLoader(current);
+                       }
+                       contextLoader = null;
+               }
+
+               @Override
+               public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
+                       Thread thread = Thread.currentThread();
+                       ClassLoader current = thread.getContextClassLoader();
+                       thread.setContextClassLoader(contextLoader);
+                       try {
+                               httpServiceServlet.service(req, res);
+                       } finally {
+                               thread.setContextClassLoader(current);
+                       }
+               }
+
+               @Override
+               public ServletConfig getServletConfig() {
+                       return httpServiceServlet.getServletConfig();
+               }
+
+               @Override
+               public String getServletInfo() {
+                       return httpServiceServlet.getServletInfo();
+               }
+
+               @Override
+               public void sessionCreated(HttpSessionEvent event) {
+                       // Nothing to do.
+               }
+
+               @Override
+               public void sessionDestroyed(HttpSessionEvent event) {
+                       Thread thread = Thread.currentThread();
+                       ClassLoader current = thread.getContextClassLoader();
+                       thread.setContextClassLoader(contextLoader);
+                       try {
+                               sessionDestroyed.invoke(httpServiceServlet, event.getSession().getId());
+                       } catch (IllegalAccessException | IllegalArgumentException e) {
+                               // not likely
+                       } catch (InvocationTargetException e) {
+                               throw new RuntimeException(e.getCause());
+                       } finally {
+                               thread.setContextClassLoader(current);
+                       }
+               }
+
+               @Override
+               public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
+                       Thread thread = Thread.currentThread();
+                       ClassLoader current = thread.getContextClassLoader();
+                       thread.setContextClassLoader(contextLoader);
+                       try {
+                               sessionIdChanged.invoke(httpServiceServlet, oldSessionId);
+                       } catch (IllegalAccessException | IllegalArgumentException e) {
+                               // not likely
+                       } catch (InvocationTargetException e) {
+                               throw new RuntimeException(e.getCause());
+                       } finally {
+                               thread.setContextClassLoader(current);
+                       }
+               }
+       }
+
+}
index 97321915ac29f7e7aaa65d480fe1683566f97ecf..e7a1ac176d508ccf62187e6c152cde32f2324ab8 100644 (file)
@@ -1,5 +1,6 @@
 package org.argeo.cms.servlet.internal.jetty;
 
+import java.io.File;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.Map;
@@ -18,6 +19,7 @@ import org.argeo.cms.websocket.javax.server.TestEndpoint;
 import org.argeo.util.LangUtils;
 import org.eclipse.equinox.http.jetty.JettyConfigurator;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceReference;
 import org.osgi.util.tracker.ServiceTracker;
@@ -38,7 +40,7 @@ public class JettyConfig {
                // We need to start asynchronously so that Jetty bundle get started by lazy init
                // due to the non-configurable behaviour of its activator
                ForkJoinPool.commonPool().execute(() -> {
-                       Dictionary<String, ?> properties = getHttpServerConfig();
+                       Dictionary<String, Object> properties = getHttpServerConfig();
                        startServer(properties);
                });
 
@@ -90,7 +92,7 @@ public class JettyConfig {
 
        }
 
-       public void startServer(Dictionary<String, ?> properties) {
+       public void startServer(Dictionary<String, Object> properties) {
                // Explicitly configures Jetty so that the default server is not started by the
                // activator of the Equinox Jetty bundle.
                Map<String, String> config = LangUtils.dictToStringMap(properties);
@@ -105,38 +107,53 @@ public class JettyConfig {
                        }
                }
 
-               long begin = System.currentTimeMillis();
-               int tryCount = 60;
-               try {
-                       while (tryCount > 0) {
-                               try {
-                                       // FIXME deal with multiple ids
-                                       JettyConfigurator.startServer(CmsConstants.DEFAULT, new Hashtable<>(config));
-
-                                       Object httpPort = config.get(JettyHttpConstants.HTTP_PORT);
-                                       Object httpsPort = config.get(JettyHttpConstants.HTTPS_PORT);
-                                       log.info(httpPortsMsg(httpPort, httpsPort));
-
-                                       // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
-                                       // configuration is not cleaned
-                                       FrameworkUtil.getBundle(JettyConfigurator.class).start();
-                                       return;
-                               } catch (IllegalStateException e) {
-                                       // e.printStackTrace();
-                                       // Jetty may not be ready
-                                       try {
-                                               Thread.sleep(1000);
-                                       } catch (Exception e1) {
-                                               // silent
-                                       }
-                                       tryCount--;
-                               }
-                       }
-                       long duration = System.currentTimeMillis() - begin;
-                       log.error("Gave up with starting Jetty server after " + (duration / 1000) + " s");
-               } catch (Exception e) {
-                       log.error("Cannot start default Jetty server with config " + properties, e);
-               }
+               properties.put(Constants.SERVICE_PID, "default");
+               File jettyWorkDir = new File(bc.getDataFile(""), "jettywork"); //$NON-NLS-1$
+               jettyWorkDir.mkdir();
+
+//             HttpServerManager serverManager = new HttpServerManager(jettyWorkDir);
+//             try {
+//                     serverManager.updated("default", properties);
+//             } catch (ConfigurationException e) {
+//                     // TODO Auto-generated catch block
+//                     e.printStackTrace();
+//             }
+               Object httpPort = config.get(JettyHttpConstants.HTTP_PORT);
+               Object httpsPort = config.get(JettyHttpConstants.HTTPS_PORT);
+               log.info(httpPortsMsg(httpPort, httpsPort));
+
+//             long begin = System.currentTimeMillis();
+//             int tryCount = 60;
+//             try {
+//                     while (tryCount > 0) {
+//                             try {
+//                                     // FIXME deal with multiple ids
+//                                     JettyConfigurator.startServer(CmsConstants.DEFAULT, new Hashtable<>(config));
+//
+//                                     Object httpPort = config.get(JettyHttpConstants.HTTP_PORT);
+//                                     Object httpsPort = config.get(JettyHttpConstants.HTTPS_PORT);
+//                                     log.info(httpPortsMsg(httpPort, httpsPort));
+//
+//                                     // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
+//                                     // configuration is not cleaned
+//                                     FrameworkUtil.getBundle(JettyConfigurator.class).start();
+//                                     return;
+//                             } catch (IllegalStateException e) {
+//                                     // e.printStackTrace();
+//                                     // Jetty may not be ready
+//                                     try {
+//                                             Thread.sleep(1000);
+//                                     } catch (Exception e1) {
+//                                             // silent
+//                                     }
+//                                     tryCount--;
+//                             }
+//                     }
+//                     long duration = System.currentTimeMillis() - begin;
+//                     log.error("Gave up with starting Jetty server after " + (duration / 1000) + " s");
+//             } catch (Exception e) {
+//                     log.error("Cannot start default Jetty server with config " + properties, e);
+//             }
 
        }
 
diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyServiceFactory.java b/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyServiceFactory.java
deleted file mode 100644 (file)
index 005af74..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-package org.argeo.cms.servlet.internal.jetty;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Map;
-
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
-import javax.websocket.server.ServerEndpointConfig;
-
-import org.argeo.api.cms.CmsConstants;
-import org.argeo.api.cms.CmsLog;
-import org.argeo.cms.websocket.javax.server.CmsWebSocketConfigurator;
-import org.argeo.cms.websocket.javax.server.TestEndpoint;
-import org.argeo.util.LangUtils;
-import org.eclipse.equinox.http.jetty.JettyConfigurator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.cm.ConfigurationException;
-import org.osgi.service.cm.ManagedServiceFactory;
-import org.osgi.util.tracker.ServiceTracker;
-
-@Deprecated
-public class JettyServiceFactory implements ManagedServiceFactory {
-       private final static CmsLog log = CmsLog.getLog(JettyServiceFactory.class);
-
-       final static String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer";
-       // Argeo specific
-       final static String WEBSOCKET_ENABLED = "websocket.enabled";
-
-       private final BundleContext bc = FrameworkUtil.getBundle(JettyServiceFactory.class).getBundleContext();
-
-       public void start() {
-               ServiceTracker<ServerContainer, ServerContainer> serverSt = new ServiceTracker<ServerContainer, ServerContainer>(
-                               bc, ServerContainer.class, null) {
-
-                       @Override
-                       public ServerContainer addingService(ServiceReference<ServerContainer> reference) {
-                               ServerContainer serverContainer = super.addingService(reference);
-
-                               BundleContext bc = reference.getBundle().getBundleContext();
-                               ServiceReference<ServerEndpointConfig.Configurator> srConfigurator = bc
-                                               .getServiceReference(ServerEndpointConfig.Configurator.class);
-                               ServerEndpointConfig.Configurator endpointConfigurator = bc.getService(srConfigurator);
-                               ServerEndpointConfig config = ServerEndpointConfig.Builder
-                                               .create(TestEndpoint.class, "/ws/test/events/").configurator(endpointConfigurator).build();
-                               try {
-                                       serverContainer.addEndpoint(config);
-                               } catch (DeploymentException e) {
-                                       throw new IllegalStateException("Cannot initalise the WebSocket server runtime.", e);
-                               }
-                               return serverContainer;
-                       }
-
-               };
-               serverSt.open();
-       }
-
-       @Override
-       public String getName() {
-               return "Jetty Service Factory";
-       }
-
-       @Override
-       public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
-               // Explicitly configures Jetty so that the default server is not started by the
-               // activator of the Equinox Jetty bundle.
-               Map<String, String> config = LangUtils.dictToStringMap(properties);
-               if (!config.isEmpty()) {
-                       config.put("customizer.class", CMS_JETTY_CUSTOMIZER_CLASS);
-
-                       // TODO centralise with Jetty extender
-                       Object webSocketEnabled = config.get(WEBSOCKET_ENABLED);
-                       if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
-                               bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
-                               config.put(WEBSOCKET_ENABLED, "true");
-                       }
-               }
-
-               int tryCount = 60;
-               try {
-                       tryGettyJetty: while (tryCount > 0) {
-                               try {
-                                       // FIXME deal with multiple ids
-                                       JettyConfigurator.startServer(CmsConstants.DEFAULT, new Hashtable<>(config));
-                                       // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
-                                       // configuration is not cleaned
-                                       FrameworkUtil.getBundle(JettyConfigurator.class).start();
-                                       break tryGettyJetty;
-                               } catch (IllegalStateException e) {
-                                       // Jetty may not be ready
-                                       try {
-                                               Thread.sleep(1000);
-                                       } catch (Exception e1) {
-                                               // silent
-                                       }
-                                       tryCount--;
-                               }
-                       }
-               } catch (Exception e) {
-                       log.error("Cannot start default Jetty server with config " + properties, e);
-               }
-
-       }
-
-       @Override
-       public void deleted(String pid) {
-       }
-
-       public void stop() {
-               try {
-                       JettyConfigurator.stopServer(CmsConstants.DEFAULT);
-               } catch (Exception e) {
-                       log.error("Cannot stop default Jetty server.", e);
-               }
-
-       }
-
-}
index ab291b5456ee2f5a5230c81137462bfe474e760d..7be23fc0f595017f3147d8fd4fc6bd06eef1b140 100644 (file)
@@ -52,11 +52,11 @@ public class CmsJettyCustomizer extends JettyCustomizer {
                if (httpsConnector != null)
                        for (ConnectionFactory connectionFactory : httpsConnector.getConnectionFactories()) {
                                if (connectionFactory instanceof SslConnectionFactory) {
-                                       SslContextFactory.Server sslConnectionFactory = ((SslConnectionFactory) connectionFactory)
+                                       SslContextFactory.Server sslContextFactory = ((SslConnectionFactory) connectionFactory)
                                                        .getSslContextFactory();
-                                       sslConnectionFactory.setTrustStorePath((String) settings.get(SSL_TRUSTSTORE));
-                                       sslConnectionFactory.setTrustStoreType((String) settings.get(SSL_TRUSTSTORETYPE));
-                                       sslConnectionFactory.setTrustStorePassword((String) settings.get(SSL_TRUSTSTOREPASSWORD));
+                                       sslContextFactory.setTrustStorePath((String) settings.get(SSL_TRUSTSTORE));
+                                       sslContextFactory.setTrustStoreType((String) settings.get(SSL_TRUSTSTORETYPE));
+                                       sslContextFactory.setTrustStorePassword((String) settings.get(SSL_TRUSTSTOREPASSWORD));
                                }
                        }
                return super.customizeHttpsConnector(connector, settings);
diff --git a/org.argeo.cms.lib.jetty/.settings/org.eclipse.jdt.core.prefs b/org.argeo.cms.lib.jetty/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644 (file)
index 62ef348..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
-org.eclipse.jdt.core.compiler.compliance=17
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
-org.eclipse.jdt.core.compiler.release=enabled
-org.eclipse.jdt.core.compiler.source=17
diff --git a/org.argeo.cms.lib.jetty/.settings/org.eclipse.pde.core.prefs b/org.argeo.cms.lib.jetty/.settings/org.eclipse.pde.core.prefs
deleted file mode 100644 (file)
index e8ff8be..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-eclipse.preferences.version=1
-pluginProject.equinox=false
-pluginProject.extensions=false
-resolve.requirebundle=false
index d926017398709a658b2816129ac07e71444faa51..171059dfa1e1dd368902fc2581074d5cdb7a1033 100644 (file)
@@ -1,9 +1,5 @@
 Import-Package: \
 javax.servlet.http,\
-org.eclipse.jetty.util.component;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.http;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.io;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.security;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.server.handler;version="[9.4,12)";resolution:=optional,\
-org.eclipse.jetty.*;version="[9.4,12)";resolution:=optional,\
+org.eclipse.jetty.server.handler,\
+org.eclipse.jetty.util.component,\
 *
\ No newline at end of file
diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/CmsJettyServer.java
new file mode 100644 (file)
index 0000000..39fc66d
--- /dev/null
@@ -0,0 +1,247 @@
+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;
+       }
+
+}
diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/lib/jetty/CmsJettyServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/lib/jetty/CmsJettyServer.java
deleted file mode 100644 (file)
index d197a00..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.argeo.cms.lib.jetty;
-
-import java.nio.file.Path;
-
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-
-public class CmsJettyServer {
-       private Server server;
-       private ServerConnector serverConnector;
-       private Path tempDir;
-
-       public void start() {
-               server = new Server(new QueuedThreadPool(10, 1));
-               serverConnector = new ServerConnector(server);
-               serverConnector.setPort(0);
-               server.setConnectors(new Connector[] { serverConnector });
-
-               ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
-               context.setContextPath("/");
-               server.setHandler(context);
-
-               //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, "/");
-
-               try {
-                       server.start();
-               } catch (Exception e) {
-                       throw new IllegalStateException("Cannot start Jetty server", e);
-               }
-               Runtime.getRuntime().addShutdownHook(new Thread(() -> stop(), "Jetty shutdown"));
-       }
-
-       public void stop() {
-               try {
-                       serverConnector.close();
-                       server.stop();
-                       // TODO delete temp dir
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-
-       }
-}
index 8d0fff4f344101f62d7730459ab5bf753a0925e2..5191b7718d391498df9058fe6a26b64b99612bff 100644 (file)
@@ -14,6 +14,8 @@ import org.argeo.api.cli.CommandsCli;
 import org.argeo.api.cli.DescribedCommand;
 import org.argeo.api.cms.CmsApp;
 import org.argeo.api.cms.CmsContext;
+import org.argeo.api.cms.CmsState;
+import org.argeo.cms.jetty.CmsJettyServer;
 import org.argeo.cms.runtime.StaticCms;
 import org.argeo.cms.swt.app.CmsUserApp;
 import org.argeo.cms.web.CmsWebApp;
@@ -60,6 +62,7 @@ public class CmsRapCli extends CommandsCli {
 
                        Path instancePath = Paths.get(dataPath);
                        System.setProperty("osgi.instance.area", instancePath.toUri().toString());
+                       System.setProperty("argeo.http.port", "0");
 
                        StaticCms staticCms = new StaticCms() {
                                @Override
@@ -77,16 +80,17 @@ public class CmsRapCli extends CommandsCli {
                                                CmsWebApp cmsWebApp = new CmsWebApp();
                                                Component<CmsWebApp> cmsWebAppC = new Component.Builder<>(cmsWebApp) //
                                                                .addType(ApplicationConfiguration.class) //
+                                                               .addType(CmsWebApp.class) //
                                                                .addDependency(cmsAppC.getType(CmsApp.class), cmsWebApp::setCmsApp, null) //
                                                                .build(register);
 
-                                               RwtRunner rwtRunner = new RwtRunner();
-                                               Component<RwtRunner> rwtRunnerC = new Component.Builder<>(rwtRunner) //
-                                                               .addActivation(rwtRunner::init) //
-                                                               .addDeactivation(rwtRunner::destroy) //
-                                                               .addType(RwtRunner.class) //
-                                                               .addDependency(cmsWebAppC.getType(ApplicationConfiguration.class),
-                                                                               rwtRunner::setApplicationConfiguration, null) //
+                                               RapJettyServer rwtRunner = new RapJettyServer();
+                                               Component<RapJettyServer> rwtRunnerC = new Component.Builder<>(rwtRunner) //
+                                                               .addActivation(rwtRunner::start) //
+                                                               .addDeactivation(rwtRunner::stop) //
+                                                               .addType(CmsJettyServer.class) //
+                                                               .addDependency(register.getSingleton(CmsState.class), rwtRunner::setCmsState, null) //
+                                                               .addDependency(cmsWebAppC.getType(CmsWebApp.class), rwtRunner::setCmsWebApp, null) //
                                                                .build(register);
                                        }
                                }
@@ -102,8 +106,9 @@ public class CmsRapCli extends CommandsCli {
                                try {
                                        // open browser in app mode
                                        Thread.sleep(2000);// wait for RWT to be ready
-                                       Runtime.getRuntime().exec("google-chrome --app=http://localhost:"
-                                                       + staticCms.getComponentRegister().getObject(RwtRunner.class).getEffectivePort() + "/data");
+                                       String browserCommand = "google-chrome --app=http://localhost:"
+                                                       + staticCms.getComponentRegister().getObject(CmsJettyServer.class).getHttpPort() + "/data";
+                                       Runtime.getRuntime().exec(browserCommand);
                                } catch (InterruptedException | IOException e) {
                                        e.printStackTrace();
                                }
diff --git a/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RapJettyServer.java b/swt/rap/org.argeo.cms.swt.rap.cli/src/org/argeo/cms/swt/rap/cli/RapJettyServer.java
new file mode 100644 (file)
index 0000000..5b3337a
--- /dev/null
@@ -0,0 +1,59 @@
+package org.argeo.cms.swt.rap.cli;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+
+import org.argeo.cms.jetty.CmsJettyServer;
+import org.argeo.cms.web.CmsWebApp;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.rap.rwt.application.ApplicationRunner;
+import org.eclipse.rap.rwt.engine.RWTServlet;
+
+public class RapJettyServer extends CmsJettyServer {
+       private CmsWebApp cmsWebApp;
+
+       @Override
+       protected void addServlets(ServletContextHandler servletContextHandler) throws ServletException {
+               // rwt-resources requires a file system
+               try {
+                       Path tempDir = Files.createTempDirectory("argeo-rwtRunner");
+                       servletContextHandler.setBaseResource(Resource.newResource(tempDir.resolve("www").toString()));
+               } catch (IOException e) {
+                       throw new IllegalStateException("Cannot create temporary directory", e);
+               }
+               servletContextHandler.addEventListener(new ServletContextListener() {
+                       ApplicationRunner applicationRunner;
+
+                       @Override
+                       public void contextInitialized(ServletContextEvent sce) {
+                               applicationRunner = new ApplicationRunner(cmsWebApp, sce.getServletContext());
+                               applicationRunner.start();
+                       }
+
+                       @Override
+                       public void contextDestroyed(ServletContextEvent sce) {
+                               applicationRunner.stop();
+                       }
+               });
+               for (String uiName : cmsWebApp.getCmsApp().getUiNames())
+                       servletContextHandler.addServlet(new ServletHolder(new RWTServlet()), "/" + uiName);
+
+               // Required to serve rwt-resources. It is important that this is last.
+               ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
+               servletContextHandler.addServlet(holderPwd, "/");
+
+       }
+
+       public void setCmsWebApp(CmsWebApp cmsWebApp) {
+               this.cmsWebApp = cmsWebApp;
+       }
+
+}
index 63016040289791809506a15ce8b049af4f691e75..84a20aeb1ba749ae0df1e5010ce606cbd4a3308e 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-       <name>org.argeo.cms.ui.rap</name>
+       <name>org.argeo.cms.swt.rap</name>
        <comment></comment>
        <projects>
        </projects>
index 0270933c0d3b24ed771eebc0e35860ff0c8aceec..fec9e2485d6bdc64c3a065574994ef325916e495 100644 (file)
@@ -113,7 +113,7 @@ public class CmsWebApp implements ApplicationConfiguration, ExceptionHandler, Cm
 //                     log.debug("Published CMS web app /" + (contextName != null ? contextName : ""));
        }
 
-       CmsApp getCmsApp() {
+       public CmsApp getCmsApp() {
                return cmsApp;
        }