X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=server%2Fruntime%2Forg.argeo.server.webextender%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fserver%2Fcatalina%2FCatalinaServer.java;fp=server%2Fruntime%2Forg.argeo.server.webextender%2Fsrc%2Fmain%2Fjava%2Forg%2Fargeo%2Fserver%2Fcatalina%2FCatalinaServer.java;h=392bbfa3041326051dd8f864dc820ec15188c798;hb=13893134219a45ed91c340c89dbd75269d21da16;hp=0000000000000000000000000000000000000000;hpb=e1e76de107e054371fab6b517dcc5be621ebffc0;p=lgpl%2Fargeo-commons.git diff --git a/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/catalina/CatalinaServer.java b/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/catalina/CatalinaServer.java new file mode 100644 index 000000000..392bbfa30 --- /dev/null +++ b/server/runtime/org.argeo.server.webextender/src/main/java/org/argeo/server/catalina/CatalinaServer.java @@ -0,0 +1,219 @@ +package org.argeo.server.catalina; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.Properties; + +import javax.management.MBeanRegistration; + +import org.apache.catalina.Lifecycle; +import org.apache.catalina.Server; +import org.apache.catalina.Service; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.core.StandardService; +import org.apache.catalina.util.ServerInfo; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.naming.resources.DirContextURLStreamHandler; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.url.AbstractURLStreamHandlerService; +import org.osgi.service.url.URLConstants; +import org.osgi.service.url.URLStreamHandlerService; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + +public class CatalinaServer implements DisposableBean,InitializingBean{ + /** logger */ + private static final Log log = LogFactory.getLog(CatalinaServer.class); + + /** default XML configuration */ + private static final String DEFAULT_XML_CONF_LOCATION = "conf/default-server.xml"; + + /** user-configurable XML configuration */ + private static final String XML_CONF_LOCATION = "conf/server.xml"; + + private BundleContext bundleContext; + + private StandardService server; + + private ServiceRegistration registration, urlRegistration; + + public void afterPropertiesSet() throws Exception { + log.info("Starting " + ServerInfo.getServerInfo() + " ..."); + + // default startup procedure + ClassLoader cl = CatalinaServer.class.getClassLoader(); + Thread current = Thread.currentThread(); + ClassLoader old = current.getContextClassLoader(); + + try { + current.setContextClassLoader(cl); + + server = createCatalinaServer(bundleContext.getBundle()); + + server.start(); + + Connector[] connectors = server.findConnectors(); + for (int i = 0; i < connectors.length; i++) { + Connector conn = connectors[i]; + log.info("Succesfully started " + ServerInfo.getServerInfo() + " @ " + conn.getDomain() + ":" + + conn.getPort()); + } + + // register URL service + urlRegistration = registerTomcatJNDIUrlService(); + // publish server as an OSGi service + registration = publishServerAsAService(server); + log.info("Published " + ServerInfo.getServerInfo() + " as an OSGi service"); + } + catch (Exception ex) { + String msg = "Cannot start " + ServerInfo.getServerInfo(); + log.error(msg, ex); + throw new RuntimeException(msg, ex); + } + finally { + current.setContextClassLoader(old); + } + } + + public void destroy() throws Exception { + // unpublish service first + registration.unregister(); + urlRegistration.unregister(); + + log.info("Unpublished " + ServerInfo.getServerInfo() + " OSGi service"); + + // default startup procedure + ClassLoader cl = CatalinaServer.class.getClassLoader(); + Thread current = Thread.currentThread(); + ClassLoader old = current.getContextClassLoader(); + + try { + current.setContextClassLoader(cl); + //reset CCL + // current.setContextClassLoader(null); + log.info("Stopping " + ServerInfo.getServerInfo() + " ..."); + server.stop(); + log.info("Succesfully stopped " + ServerInfo.getServerInfo()); + } + catch (Exception ex) { + log.error("Cannot stop " + ServerInfo.getServerInfo(), ex); + throw ex; + } + finally { + current.setContextClassLoader(old); + } + } + + private StandardService createCatalinaServer(Bundle bundle) throws Exception { + // first try to use the XML file + URL xmlConfiguration = bundle.getResource(XML_CONF_LOCATION); + + if (xmlConfiguration != null) { + log.info("Using custom XML configuration " + xmlConfiguration); + } + else { + xmlConfiguration = bundle.getResource(DEFAULT_XML_CONF_LOCATION); + if (xmlConfiguration == null) + log.error("No XML configuration found; bailing out..."); + else + log.info("Using default XML configuration " + xmlConfiguration); + } + + return createServerFromXML(xmlConfiguration); + } + + private StandardService createServerFromXML(URL xmlConfiguration) throws IOException { + OsgiCatalina catalina = new OsgiCatalina(); + catalina.setAwait(false); + catalina.setUseShutdownHook(false); + catalina.setName("Catalina"); + catalina.setParentClassLoader(Thread.currentThread().getContextClassLoader()); + + // copy the URL file to a local temporary file (since Catalina doesn't use URL unfortunately) + File configTempFile = File.createTempFile("dm.catalina", ".cfg.xml"); + configTempFile.deleteOnExit(); + + // copy URL to temporary file + copyURLToFile(xmlConfiguration.openStream(), new FileOutputStream(configTempFile)); + log.debug("Copied configuration " + xmlConfiguration + " to temporary file " + configTempFile); + + catalina.setConfigFile(configTempFile.getAbsolutePath()); + + catalina.load(); + + Server server = catalina.getServer(); + + return (StandardService) server.findServices()[0]; + } + + private void copyURLToFile(InputStream inStream, FileOutputStream outStream) { + + int bytesRead; + byte[] buf = new byte[4096]; + try { + while ((bytesRead = inStream.read(buf)) >= 0) { + outStream.write(buf, 0, bytesRead); + } + } + catch (IOException ex) { + throw (RuntimeException) new IllegalStateException("Cannot copy URL to file").initCause(ex); + } + finally { + try { + inStream.close(); + } + catch (IOException ignore) { + } + try { + outStream.close(); + } + catch (IOException ignore) { + } + } + } + + private ServiceRegistration publishServerAsAService(StandardService server) { + Properties props = new Properties(); + // put some extra properties to easily identify the service + props.put(Constants.SERVICE_VENDOR, "Spring Dynamic Modules"); + props.put(Constants.SERVICE_DESCRIPTION, ServerInfo.getServerInfo()); + props.put(Constants.BUNDLE_VERSION, ServerInfo.getServerNumber()); + props.put(Constants.BUNDLE_NAME, bundleContext.getBundle().getSymbolicName()); + + // spring-dm specific property + props.put("org.springframework.osgi.bean.name", "tomcat-server"); + + // publish just the interfaces and the major classes (server/handlerWrapper) + String[] classes = new String[] { StandardService.class.getName(), Service.class.getName(), + MBeanRegistration.class.getName(), Lifecycle.class.getName() }; + + return bundleContext.registerService(classes, server, props); + } + + private ServiceRegistration registerTomcatJNDIUrlService() { + Properties properties = new Properties(); + properties.put(URLConstants.URL_HANDLER_PROTOCOL, "jndi"); + final URLStreamHandler handler = new DirContextURLStreamHandler(); + + return bundleContext.registerService(URLStreamHandlerService.class.getName(), + new AbstractURLStreamHandlerService() { + + private final static String EMPTY_STRING = ""; + + + public URLConnection openConnection(URL u) throws IOException { + return new URL(u, EMPTY_STRING, handler).openConnection(); + } + }, properties); + } + +}