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; } }