--- /dev/null
+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);
+ }
+
+}