org.argeo.cms.ux \
org.argeo.cms.ee \
org.argeo.cms.lib.jetty \
-org.argeo.cms.lib.equinox \
org.argeo.cms.lib.sshd \
org.argeo.cms.cli \
+osgi/equinox/org.argeo.cms.lib.equinox \
swt/org.argeo.swt.minidesktop \
swt/org.argeo.cms.swt \
swt/org.argeo.cms.e4 \
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-/bin/
-/target/
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.cms.lib.equinox</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.SchemaBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ds.core.builder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.pde.PluginNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-/MANIFEST.MF
+++ /dev/null
-<?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.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"/>
- <service>
- <provide interface="com.sun.net.httpserver.HttpServer"/>
- </service>
-</scr:component>
+++ /dev/null
-Service-Component: \
-OSGI-INF/jettyServiceFactory.xml,\
+++ /dev/null
-source.. = src/
-output.. = bin/
-bin.includes = META-INF/,\
- .
+++ /dev/null
-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;
-
-/** A {@link CmsJettyServer} integrating with Equinox HTTP framework. */
-public class EquinoxJettyServer extends CmsJettyServer {
- private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
-
- @Override
- protected void addServlets(ServletContextHandler rootContextHandler) 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$
-
- rootContextHandler.addServlet(holder, "/*");
-
- // post-start
- SessionHandler sessionManager = rootContextHandler.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);
- }
- }
- }
-
-}
+++ /dev/null
-package org.argeo.cms.servlet.internal.jetty;
-
-import java.io.File;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.concurrent.ForkJoinPool;
-
-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.api.cms.CmsState;
-import org.argeo.cms.CmsDeployProperty;
-import org.argeo.cms.websocket.server.CmsWebSocketConfigurator;
-import org.argeo.cms.websocket.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;
-
-public class JettyConfig {
- private final static CmsLog log = CmsLog.getLog(JettyConfig.class);
-
- final static String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer";
-
- private CmsState cmsState;
-
- private final BundleContext bc = FrameworkUtil.getBundle(JettyConfig.class).getBundleContext();
-
- // private static final String JETTY_PROPERTY_PREFIX =
- // "org.eclipse.equinox.http.jetty.";
-
- public void start() {
- // 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, Object> properties = getHttpServerConfig();
- startServer(properties);
- });
-
- 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();
-
- // check initialisation
-// ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
-//
-// @Override
-// public HttpService addingService(ServiceReference<HttpService> sr) {
-// Object httpPort = sr.getProperty("http.port");
-// Object httpsPort = sr.getProperty("https.port");
-// log.info(httpPortsMsg(httpPort, httpsPort));
-// close();
-// return super.addingService(sr);
-// }
-// };
-// httpSt.open();
- }
-
- public void stop() {
- try {
- JettyConfigurator.stopServer(CmsConstants.DEFAULT);
- } catch (Exception e) {
- log.error("Cannot stop default Jetty server.", e);
- }
-
- }
-
- 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);
- if (!config.isEmpty()) {
- config.put("customizer.class", CMS_JETTY_CUSTOMIZER_CLASS);
-
- // TODO centralise with Jetty extender
- Object webSocketEnabled = config.get(CmsDeployProperty.WEBSOCKET_ENABLED.getProperty());
- if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
- bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
- // config.put(WEBSOCKET_ENABLED, "true");
- }
- }
-
- 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);
-// }
-
- }
-
- private String httpPortsMsg(Object httpPort, Object httpsPort) {
- return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : "");
- }
-
- /** Override the provided config with the framework properties */
- public Dictionary<String, Object> getHttpServerConfig() {
- String httpPort = getFrameworkProp(CmsDeployProperty.HTTP_PORT);
- String httpsPort = getFrameworkProp(CmsDeployProperty.HTTPS_PORT);
- /// TODO make it more generic
- String httpHost = getFrameworkProp(CmsDeployProperty.HOST);
-// String httpsHost = getFrameworkProp(
-// JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
- String webSocketEnabled = getFrameworkProp(CmsDeployProperty.WEBSOCKET_ENABLED);
-
- final Hashtable<String, Object> props = new Hashtable<String, Object>();
- // try {
- if (httpPort != null || httpsPort != null) {
- boolean httpEnabled = httpPort != null;
- props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
- boolean httpsEnabled = httpsPort != null;
- props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
-
- if (httpEnabled) {
- props.put(JettyHttpConstants.HTTP_PORT, httpPort);
- if (httpHost != null)
- props.put(JettyHttpConstants.HTTP_HOST, httpHost);
- }
-
- if (httpsEnabled) {
- props.put(JettyHttpConstants.HTTPS_PORT, httpsPort);
- if (httpHost != null)
- props.put(JettyHttpConstants.HTTPS_HOST, httpHost);
-
- // keystore
- props.put(JettyHttpConstants.SSL_KEYSTORETYPE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
- props.put(JettyHttpConstants.SSL_KEYSTORE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORE));
- props.put(JettyHttpConstants.SSL_PASSWORD, getFrameworkProp(CmsDeployProperty.SSL_PASSWORD));
-
- // truststore
- props.put(JettyHttpConstants.SSL_TRUSTSTORETYPE,
- getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORETYPE));
- props.put(JettyHttpConstants.SSL_TRUSTSTORE, getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORE));
- props.put(JettyHttpConstants.SSL_TRUSTSTOREPASSWORD,
- getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
-
- // client certificate authentication
- String wantClientAuth = getFrameworkProp(CmsDeployProperty.SSL_WANTCLIENTAUTH);
- if (wantClientAuth != null)
- props.put(JettyHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
- String needClientAuth = getFrameworkProp(CmsDeployProperty.SSL_NEEDCLIENTAUTH);
- if (needClientAuth != null)
- props.put(JettyHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
- }
-
- // web socket
- if (webSocketEnabled != null && webSocketEnabled.equals("true"))
- props.put(CmsDeployProperty.WEBSOCKET_ENABLED.getProperty(), true);
-
- props.put(CmsConstants.CN, CmsConstants.DEFAULT);
- }
- return props;
- }
-
- private String getFrameworkProp(CmsDeployProperty deployProperty) {
- return cmsState.getDeployProperty(deployProperty.getProperty());
- }
-
- public void setCmsState(CmsState cmsState) {
- this.cmsState = cmsState;
- }
-
-}
+++ /dev/null
-package org.argeo.cms.servlet.internal.jetty;
-
-/** Compatible with Jetty. */
-interface JettyHttpConstants {
- static final String HTTP_ENABLED = "http.enabled";
- static final String HTTP_PORT = "http.port";
- static final String HTTP_HOST = "http.host";
- static final String HTTPS_ENABLED = "https.enabled";
- static final String HTTPS_HOST = "https.host";
- static final String HTTPS_PORT = "https.port";
- static final String SSL_KEYSTORE = "ssl.keystore";
- static final String SSL_PASSWORD = "ssl.password";
- static final String SSL_KEYPASSWORD = "ssl.keypassword";
- static final String SSL_NEEDCLIENTAUTH = "ssl.needclientauth";
- static final String SSL_WANTCLIENTAUTH = "ssl.wantclientauth";
- static final String SSL_PROTOCOL = "ssl.protocol";
- static final String SSL_ALGORITHM = "ssl.algorithm";
- static final String SSL_KEYSTORETYPE = "ssl.keystoretype";
-
- // Argeo
- static final String SSL_TRUSTSTORE = "ssl.truststore";
- static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
- static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
-
-}
+++ /dev/null
-package org.argeo.equinox.jetty;
-
-import java.util.Dictionary;
-
-import javax.servlet.ServletContext;
-import javax.websocket.DeploymentException;
-import javax.websocket.server.ServerContainer;
-
-import org.eclipse.equinox.http.jetty.JettyCustomizer;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
-import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-/** Customises the Jetty HTTP server. */
-public class CmsJettyCustomizer extends JettyCustomizer {
- static final String SSL_TRUSTSTORE = "ssl.truststore";
- static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
- static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
-
- private BundleContext bc = FrameworkUtil.getBundle(CmsJettyCustomizer.class).getBundleContext();
-
- public final static String WEBSOCKET_ENABLED = "argeo.websocket.enabled";
-
- @Override
- public Object customizeContext(Object context, Dictionary<String, ?> settings) {
- // WebSocket
- Object webSocketEnabled = settings.get(WEBSOCKET_ENABLED);
- if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
- ServletContextHandler servletContextHandler = (ServletContextHandler) context;
- JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
-
- @Override
- public void accept(ServletContext servletContext, ServerContainer serverContainer)
- throws DeploymentException {
- bc.registerService(javax.websocket.server.ServerContainer.class, serverContainer, null);
- }
- });
- }
- return super.customizeContext(context, settings);
-
- }
-
- @Override
- public Object customizeHttpsConnector(Object connector, Dictionary<String, ?> settings) {
- ServerConnector httpsConnector = (ServerConnector) connector;
- if (httpsConnector != null)
- for (ConnectionFactory connectionFactory : httpsConnector.getConnectionFactories()) {
- if (connectionFactory instanceof SslConnectionFactory) {
- SslContextFactory.Server sslContextFactory = ((SslConnectionFactory) connectionFactory)
- .getSslContextFactory();
- 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);
- }
-
-}
+++ /dev/null
-/** Equinox Jetty extensions. */
-package org.argeo.equinox.jetty;
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+/bin/
+/target/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.cms.lib.equinox</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ds.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+<?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.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"/>
+ <service>
+ <provide interface="com.sun.net.httpserver.HttpServer"/>
+ </service>
+</scr:component>
--- /dev/null
+Service-Component: \
+OSGI-INF/jettyServiceFactory.xml,\
--- /dev/null
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
--- /dev/null
+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;
+
+/** A {@link CmsJettyServer} integrating with Equinox HTTP framework. */
+public class EquinoxJettyServer extends CmsJettyServer {
+ private static final String INTERNAL_CONTEXT_CLASSLOADER = "org.eclipse.equinox.http.jetty.internal.ContextClassLoader";
+
+ @Override
+ protected void addServlets(ServletContextHandler rootContextHandler) 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$
+
+ rootContextHandler.addServlet(holder, "/*");
+
+ // post-start
+ SessionHandler sessionManager = rootContextHandler.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);
+ }
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.cms.servlet.internal.jetty;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.ForkJoinPool;
+
+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.api.cms.CmsState;
+import org.argeo.cms.CmsDeployProperty;
+import org.argeo.cms.websocket.server.CmsWebSocketConfigurator;
+import org.argeo.cms.websocket.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;
+
+public class JettyConfig {
+ private final static CmsLog log = CmsLog.getLog(JettyConfig.class);
+
+ final static String CMS_JETTY_CUSTOMIZER_CLASS = "org.argeo.equinox.jetty.CmsJettyCustomizer";
+
+ private CmsState cmsState;
+
+ private final BundleContext bc = FrameworkUtil.getBundle(JettyConfig.class).getBundleContext();
+
+ // private static final String JETTY_PROPERTY_PREFIX =
+ // "org.eclipse.equinox.http.jetty.";
+
+ public void start() {
+ // 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, Object> properties = getHttpServerConfig();
+ startServer(properties);
+ });
+
+ 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();
+
+ // check initialisation
+// ServiceTracker<?, ?> httpSt = new ServiceTracker<HttpService, HttpService>(bc, HttpService.class, null) {
+//
+// @Override
+// public HttpService addingService(ServiceReference<HttpService> sr) {
+// Object httpPort = sr.getProperty("http.port");
+// Object httpsPort = sr.getProperty("https.port");
+// log.info(httpPortsMsg(httpPort, httpsPort));
+// close();
+// return super.addingService(sr);
+// }
+// };
+// httpSt.open();
+ }
+
+ public void stop() {
+ try {
+ JettyConfigurator.stopServer(CmsConstants.DEFAULT);
+ } catch (Exception e) {
+ log.error("Cannot stop default Jetty server.", e);
+ }
+
+ }
+
+ 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);
+ if (!config.isEmpty()) {
+ config.put("customizer.class", CMS_JETTY_CUSTOMIZER_CLASS);
+
+ // TODO centralise with Jetty extender
+ Object webSocketEnabled = config.get(CmsDeployProperty.WEBSOCKET_ENABLED.getProperty());
+ if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
+ bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
+ // config.put(WEBSOCKET_ENABLED, "true");
+ }
+ }
+
+ 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);
+// }
+
+ }
+
+ private String httpPortsMsg(Object httpPort, Object httpsPort) {
+ return (httpPort != null ? "HTTP " + httpPort + " " : " ") + (httpsPort != null ? "HTTPS " + httpsPort : "");
+ }
+
+ /** Override the provided config with the framework properties */
+ public Dictionary<String, Object> getHttpServerConfig() {
+ String httpPort = getFrameworkProp(CmsDeployProperty.HTTP_PORT);
+ String httpsPort = getFrameworkProp(CmsDeployProperty.HTTPS_PORT);
+ /// TODO make it more generic
+ String httpHost = getFrameworkProp(CmsDeployProperty.HOST);
+// String httpsHost = getFrameworkProp(
+// JettyConfig.JETTY_PROPERTY_PREFIX + CmsHttpConstants.HTTPS_HOST);
+ String webSocketEnabled = getFrameworkProp(CmsDeployProperty.WEBSOCKET_ENABLED);
+
+ final Hashtable<String, Object> props = new Hashtable<String, Object>();
+ // try {
+ if (httpPort != null || httpsPort != null) {
+ boolean httpEnabled = httpPort != null;
+ props.put(JettyHttpConstants.HTTP_ENABLED, httpEnabled);
+ boolean httpsEnabled = httpsPort != null;
+ props.put(JettyHttpConstants.HTTPS_ENABLED, httpsEnabled);
+
+ if (httpEnabled) {
+ props.put(JettyHttpConstants.HTTP_PORT, httpPort);
+ if (httpHost != null)
+ props.put(JettyHttpConstants.HTTP_HOST, httpHost);
+ }
+
+ if (httpsEnabled) {
+ props.put(JettyHttpConstants.HTTPS_PORT, httpsPort);
+ if (httpHost != null)
+ props.put(JettyHttpConstants.HTTPS_HOST, httpHost);
+
+ // keystore
+ props.put(JettyHttpConstants.SSL_KEYSTORETYPE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORETYPE));
+ props.put(JettyHttpConstants.SSL_KEYSTORE, getFrameworkProp(CmsDeployProperty.SSL_KEYSTORE));
+ props.put(JettyHttpConstants.SSL_PASSWORD, getFrameworkProp(CmsDeployProperty.SSL_PASSWORD));
+
+ // truststore
+ props.put(JettyHttpConstants.SSL_TRUSTSTORETYPE,
+ getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORETYPE));
+ props.put(JettyHttpConstants.SSL_TRUSTSTORE, getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTORE));
+ props.put(JettyHttpConstants.SSL_TRUSTSTOREPASSWORD,
+ getFrameworkProp(CmsDeployProperty.SSL_TRUSTSTOREPASSWORD));
+
+ // client certificate authentication
+ String wantClientAuth = getFrameworkProp(CmsDeployProperty.SSL_WANTCLIENTAUTH);
+ if (wantClientAuth != null)
+ props.put(JettyHttpConstants.SSL_WANTCLIENTAUTH, Boolean.parseBoolean(wantClientAuth));
+ String needClientAuth = getFrameworkProp(CmsDeployProperty.SSL_NEEDCLIENTAUTH);
+ if (needClientAuth != null)
+ props.put(JettyHttpConstants.SSL_NEEDCLIENTAUTH, Boolean.parseBoolean(needClientAuth));
+ }
+
+ // web socket
+ if (webSocketEnabled != null && webSocketEnabled.equals("true"))
+ props.put(CmsDeployProperty.WEBSOCKET_ENABLED.getProperty(), true);
+
+ props.put(CmsConstants.CN, CmsConstants.DEFAULT);
+ }
+ return props;
+ }
+
+ private String getFrameworkProp(CmsDeployProperty deployProperty) {
+ return cmsState.getDeployProperty(deployProperty.getProperty());
+ }
+
+ public void setCmsState(CmsState cmsState) {
+ this.cmsState = cmsState;
+ }
+
+}
--- /dev/null
+package org.argeo.cms.servlet.internal.jetty;
+
+/** Compatible with Jetty. */
+interface JettyHttpConstants {
+ static final String HTTP_ENABLED = "http.enabled";
+ static final String HTTP_PORT = "http.port";
+ static final String HTTP_HOST = "http.host";
+ static final String HTTPS_ENABLED = "https.enabled";
+ static final String HTTPS_HOST = "https.host";
+ static final String HTTPS_PORT = "https.port";
+ static final String SSL_KEYSTORE = "ssl.keystore";
+ static final String SSL_PASSWORD = "ssl.password";
+ static final String SSL_KEYPASSWORD = "ssl.keypassword";
+ static final String SSL_NEEDCLIENTAUTH = "ssl.needclientauth";
+ static final String SSL_WANTCLIENTAUTH = "ssl.wantclientauth";
+ static final String SSL_PROTOCOL = "ssl.protocol";
+ static final String SSL_ALGORITHM = "ssl.algorithm";
+ static final String SSL_KEYSTORETYPE = "ssl.keystoretype";
+
+ // Argeo
+ static final String SSL_TRUSTSTORE = "ssl.truststore";
+ static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
+ static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
+
+}
--- /dev/null
+package org.argeo.equinox.jetty;
+
+import java.util.Dictionary;
+
+import javax.servlet.ServletContext;
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+
+import org.eclipse.equinox.http.jetty.JettyCustomizer;
+import org.eclipse.jetty.server.ConnectionFactory;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.javax.server.config.JavaxWebSocketServletContainerInitializer.Configurator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/** Customises the Jetty HTTP server. */
+public class CmsJettyCustomizer extends JettyCustomizer {
+ static final String SSL_TRUSTSTORE = "ssl.truststore";
+ static final String SSL_TRUSTSTOREPASSWORD = "ssl.truststorepassword";
+ static final String SSL_TRUSTSTORETYPE = "ssl.truststoretype";
+
+ private BundleContext bc = FrameworkUtil.getBundle(CmsJettyCustomizer.class).getBundleContext();
+
+ public final static String WEBSOCKET_ENABLED = "argeo.websocket.enabled";
+
+ @Override
+ public Object customizeContext(Object context, Dictionary<String, ?> settings) {
+ // WebSocket
+ Object webSocketEnabled = settings.get(WEBSOCKET_ENABLED);
+ if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
+ ServletContextHandler servletContextHandler = (ServletContextHandler) context;
+ JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, new Configurator() {
+
+ @Override
+ public void accept(ServletContext servletContext, ServerContainer serverContainer)
+ throws DeploymentException {
+ bc.registerService(javax.websocket.server.ServerContainer.class, serverContainer, null);
+ }
+ });
+ }
+ return super.customizeContext(context, settings);
+
+ }
+
+ @Override
+ public Object customizeHttpsConnector(Object connector, Dictionary<String, ?> settings) {
+ ServerConnector httpsConnector = (ServerConnector) connector;
+ if (httpsConnector != null)
+ for (ConnectionFactory connectionFactory : httpsConnector.getConnectionFactories()) {
+ if (connectionFactory instanceof SslConnectionFactory) {
+ SslContextFactory.Server sslContextFactory = ((SslConnectionFactory) connectionFactory)
+ .getSslContextFactory();
+ 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);
+ }
+
+}
--- /dev/null
+/** Equinox Jetty extensions. */
+package org.argeo.equinox.jetty;
\ No newline at end of file