From: Mathieu Baudier Date: Tue, 1 Nov 2022 08:06:48 +0000 (+0100) Subject: Move Equinox specific code to the appropriate variant X-Git-Tag: v2.3.11~51 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=40ae72d5e9f2e15ea2d1ac6bbed7f0f2e312cbdc Move Equinox specific code to the appropriate variant --- diff --git a/Makefile b/Makefile index 57bb5b38c..3d881b870 100644 --- a/Makefile +++ b/Makefile @@ -17,9 +17,9 @@ org.argeo.cms \ 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 \ diff --git a/org.argeo.cms.lib.equinox/.classpath b/org.argeo.cms.lib.equinox/.classpath deleted file mode 100644 index 81fe078c2..000000000 --- a/org.argeo.cms.lib.equinox/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/org.argeo.cms.lib.equinox/.gitignore b/org.argeo.cms.lib.equinox/.gitignore deleted file mode 100644 index 09e3bc9b2..000000000 --- a/org.argeo.cms.lib.equinox/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin/ -/target/ diff --git a/org.argeo.cms.lib.equinox/.project b/org.argeo.cms.lib.equinox/.project deleted file mode 100644 index c551d5dff..000000000 --- a/org.argeo.cms.lib.equinox/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - org.argeo.cms.lib.equinox - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - org.eclipse.pde.ds.core.builder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/org.argeo.cms.lib.equinox/META-INF/.gitignore b/org.argeo.cms.lib.equinox/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.cms.lib.equinox/META-INF/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/MANIFEST.MF diff --git a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml b/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml deleted file mode 100644 index 6a1336220..000000000 --- a/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/org.argeo.cms.lib.equinox/bnd.bnd b/org.argeo.cms.lib.equinox/bnd.bnd deleted file mode 100644 index 2c83158e2..000000000 --- a/org.argeo.cms.lib.equinox/bnd.bnd +++ /dev/null @@ -1,2 +0,0 @@ -Service-Component: \ -OSGI-INF/jettyServiceFactory.xml,\ diff --git a/org.argeo.cms.lib.equinox/build.properties b/org.argeo.cms.lib.equinox/build.properties deleted file mode 100644 index 34d2e4d2d..000000000 --- a/org.argeo.cms.lib.equinox/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . 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 deleted file mode 100644 index e6595a05e..000000000 --- a/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java +++ /dev/null @@ -1,151 +0,0 @@ -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); - } - } - } - -} diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java b/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java deleted file mode 100644 index 50be8b7a7..000000000 --- a/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java +++ /dev/null @@ -1,231 +0,0 @@ -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 properties = getHttpServerConfig(); - startServer(properties); - }); - - ServiceTracker serverSt = new ServiceTracker( - bc, ServerContainer.class, null) { - - @Override - public ServerContainer addingService(ServiceReference reference) { - ServerContainer serverContainer = super.addingService(reference); - - BundleContext bc = reference.getBundle().getBundleContext(); - ServiceReference 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(bc, HttpService.class, null) { -// -// @Override -// public HttpService addingService(ServiceReference 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 properties) { - // Explicitly configures Jetty so that the default server is not started by the - // activator of the Equinox Jetty bundle. - Map 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 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 props = new Hashtable(); - // 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; - } - -} diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java b/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java deleted file mode 100644 index 8ceb358dd..000000000 --- a/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java +++ /dev/null @@ -1,25 +0,0 @@ -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"; - -} diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java b/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java deleted file mode 100644 index 7be23fc0f..000000000 --- a/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java +++ /dev/null @@ -1,65 +0,0 @@ -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 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 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); - } - -} diff --git a/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java b/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java deleted file mode 100644 index 41c8ce9b0..000000000 --- a/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Equinox Jetty extensions. */ -package org.argeo.equinox.jetty; \ No newline at end of file diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/.classpath b/osgi/equinox/org.argeo.cms.lib.equinox/.classpath new file mode 100644 index 000000000..81fe078c2 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/.gitignore b/osgi/equinox/org.argeo.cms.lib.equinox/.gitignore new file mode 100644 index 000000000..09e3bc9b2 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/target/ diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/.project b/osgi/equinox/org.argeo.cms.lib.equinox/.project new file mode 100644 index 000000000..c551d5dff --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/.project @@ -0,0 +1,33 @@ + + + org.argeo.cms.lib.equinox + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.ds.core.builder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/META-INF/.gitignore b/osgi/equinox/org.argeo.cms.lib.equinox/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml b/osgi/equinox/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml new file mode 100644 index 000000000..6a1336220 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/OSGI-INF/jettyServiceFactory.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/bnd.bnd b/osgi/equinox/org.argeo.cms.lib.equinox/bnd.bnd new file mode 100644 index 000000000..2c83158e2 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/bnd.bnd @@ -0,0 +1,2 @@ +Service-Component: \ +OSGI-INF/jettyServiceFactory.xml,\ diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/build.properties b/osgi/equinox/org.argeo.cms.lib.equinox/build.properties new file mode 100644 index 000000000..34d2e4d2d --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java new file mode 100644 index 000000000..e6595a05e --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/EquinoxJettyServer.java @@ -0,0 +1,151 @@ +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); + } + } + } + +} diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java new file mode 100644 index 000000000..50be8b7a7 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyConfig.java @@ -0,0 +1,231 @@ +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 properties = getHttpServerConfig(); + startServer(properties); + }); + + ServiceTracker serverSt = new ServiceTracker( + bc, ServerContainer.class, null) { + + @Override + public ServerContainer addingService(ServiceReference reference) { + ServerContainer serverContainer = super.addingService(reference); + + BundleContext bc = reference.getBundle().getBundleContext(); + ServiceReference 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(bc, HttpService.class, null) { +// +// @Override +// public HttpService addingService(ServiceReference 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 properties) { + // Explicitly configures Jetty so that the default server is not started by the + // activator of the Equinox Jetty bundle. + Map 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 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 props = new Hashtable(); + // 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; + } + +} diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java new file mode 100644 index 000000000..8ceb358dd --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/servlet/internal/jetty/JettyHttpConstants.java @@ -0,0 +1,25 @@ +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"; + +} diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java new file mode 100644 index 000000000..7be23fc0f --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/CmsJettyCustomizer.java @@ -0,0 +1,65 @@ +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 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 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); + } + +} diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java new file mode 100644 index 000000000..41c8ce9b0 --- /dev/null +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/equinox/jetty/package-info.java @@ -0,0 +1,2 @@ +/** Equinox Jetty extensions. */ +package org.argeo.equinox.jetty; \ No newline at end of file