1 package org
.argeo
.server
.catalina
;
4 import java
.io
.FileOutputStream
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
8 import java
.net
.URLConnection
;
9 import java
.net
.URLStreamHandler
;
10 import java
.util
.Properties
;
12 import javax
.management
.MBeanRegistration
;
14 import org
.apache
.catalina
.Lifecycle
;
15 import org
.apache
.catalina
.Server
;
16 import org
.apache
.catalina
.Service
;
17 import org
.apache
.catalina
.connector
.Connector
;
18 import org
.apache
.catalina
.core
.StandardService
;
19 import org
.apache
.catalina
.util
.ServerInfo
;
20 import org
.apache
.commons
.logging
.Log
;
21 import org
.apache
.commons
.logging
.LogFactory
;
22 import org
.apache
.naming
.resources
.DirContextURLStreamHandler
;
23 import org
.osgi
.framework
.Bundle
;
24 import org
.osgi
.framework
.BundleContext
;
25 import org
.osgi
.framework
.Constants
;
26 import org
.osgi
.framework
.ServiceRegistration
;
27 import org
.osgi
.service
.url
.AbstractURLStreamHandlerService
;
28 import org
.osgi
.service
.url
.URLConstants
;
29 import org
.osgi
.service
.url
.URLStreamHandlerService
;
30 import org
.springframework
.beans
.factory
.DisposableBean
;
31 import org
.springframework
.beans
.factory
.InitializingBean
;
33 public class CatalinaServer
implements DisposableBean
,InitializingBean
{
35 private static final Log log
= LogFactory
.getLog(CatalinaServer
.class);
37 /** default XML configuration */
38 private static final String DEFAULT_XML_CONF_LOCATION
= "conf/default-server.xml";
40 /** user-configurable XML configuration */
41 private static final String XML_CONF_LOCATION
= "conf/server.xml";
43 private BundleContext bundleContext
;
45 private StandardService server
;
47 private ServiceRegistration registration
, urlRegistration
;
49 public void afterPropertiesSet() throws Exception
{
50 log
.info("Starting " + ServerInfo
.getServerInfo() + " ...");
52 // default startup procedure
53 ClassLoader cl
= CatalinaServer
.class.getClassLoader();
54 Thread current
= Thread
.currentThread();
55 ClassLoader old
= current
.getContextClassLoader();
58 current
.setContextClassLoader(cl
);
60 server
= createCatalinaServer(bundleContext
.getBundle());
64 Connector
[] connectors
= server
.findConnectors();
65 for (int i
= 0; i
< connectors
.length
; i
++) {
66 Connector conn
= connectors
[i
];
67 log
.info("Succesfully started " + ServerInfo
.getServerInfo() + " @ " + conn
.getDomain() + ":"
71 // register URL service
72 urlRegistration
= registerTomcatJNDIUrlService();
73 // publish server as an OSGi service
74 registration
= publishServerAsAService(server
);
75 log
.info("Published " + ServerInfo
.getServerInfo() + " as an OSGi service");
77 catch (Exception ex
) {
78 String msg
= "Cannot start " + ServerInfo
.getServerInfo();
80 throw new RuntimeException(msg
, ex
);
83 current
.setContextClassLoader(old
);
87 public void destroy() throws Exception
{
88 // unpublish service first
89 registration
.unregister();
90 urlRegistration
.unregister();
92 log
.info("Unpublished " + ServerInfo
.getServerInfo() + " OSGi service");
94 // default startup procedure
95 ClassLoader cl
= CatalinaServer
.class.getClassLoader();
96 Thread current
= Thread
.currentThread();
97 ClassLoader old
= current
.getContextClassLoader();
100 current
.setContextClassLoader(cl
);
102 // current.setContextClassLoader(null);
103 log
.info("Stopping " + ServerInfo
.getServerInfo() + " ...");
105 log
.info("Succesfully stopped " + ServerInfo
.getServerInfo());
107 catch (Exception ex
) {
108 log
.error("Cannot stop " + ServerInfo
.getServerInfo(), ex
);
112 current
.setContextClassLoader(old
);
116 private StandardService
createCatalinaServer(Bundle bundle
) throws Exception
{
117 // first try to use the XML file
118 URL xmlConfiguration
= bundle
.getResource(XML_CONF_LOCATION
);
120 if (xmlConfiguration
!= null) {
121 log
.info("Using custom XML configuration " + xmlConfiguration
);
124 xmlConfiguration
= bundle
.getResource(DEFAULT_XML_CONF_LOCATION
);
125 if (xmlConfiguration
== null)
126 log
.error("No XML configuration found; bailing out...");
128 log
.info("Using default XML configuration " + xmlConfiguration
);
131 return createServerFromXML(xmlConfiguration
);
134 private StandardService
createServerFromXML(URL xmlConfiguration
) throws IOException
{
135 OsgiCatalina catalina
= new OsgiCatalina();
136 catalina
.setAwait(false);
137 catalina
.setUseShutdownHook(false);
138 catalina
.setName("Catalina");
139 catalina
.setParentClassLoader(Thread
.currentThread().getContextClassLoader());
141 // copy the URL file to a local temporary file (since Catalina doesn't use URL unfortunately)
142 File configTempFile
= File
.createTempFile("dm.catalina", ".cfg.xml");
143 configTempFile
.deleteOnExit();
145 // copy URL to temporary file
146 copyURLToFile(xmlConfiguration
.openStream(), new FileOutputStream(configTempFile
));
147 log
.debug("Copied configuration " + xmlConfiguration
+ " to temporary file " + configTempFile
);
149 catalina
.setConfigFile(configTempFile
.getAbsolutePath());
153 Server server
= catalina
.getServer();
155 return (StandardService
) server
.findServices()[0];
158 private void copyURLToFile(InputStream inStream
, FileOutputStream outStream
) {
161 byte[] buf
= new byte[4096];
163 while ((bytesRead
= inStream
.read(buf
)) >= 0) {
164 outStream
.write(buf
, 0, bytesRead
);
167 catch (IOException ex
) {
168 throw (RuntimeException
) new IllegalStateException("Cannot copy URL to file").initCause(ex
);
174 catch (IOException ignore
) {
179 catch (IOException ignore
) {
184 private ServiceRegistration
publishServerAsAService(StandardService server
) {
185 Properties props
= new Properties();
186 // put some extra properties to easily identify the service
187 props
.put(Constants
.SERVICE_VENDOR
, "Spring Dynamic Modules");
188 props
.put(Constants
.SERVICE_DESCRIPTION
, ServerInfo
.getServerInfo());
189 props
.put(Constants
.BUNDLE_VERSION
, ServerInfo
.getServerNumber());
190 props
.put(Constants
.BUNDLE_NAME
, bundleContext
.getBundle().getSymbolicName());
192 // spring-dm specific property
193 props
.put("org.springframework.osgi.bean.name", "tomcat-server");
195 // publish just the interfaces and the major classes (server/handlerWrapper)
196 String
[] classes
= new String
[] { StandardService
.class.getName(), Service
.class.getName(),
197 MBeanRegistration
.class.getName(), Lifecycle
.class.getName() };
199 return bundleContext
.registerService(classes
, server
, props
);
202 private ServiceRegistration
registerTomcatJNDIUrlService() {
203 Properties properties
= new Properties();
204 properties
.put(URLConstants
.URL_HANDLER_PROTOCOL
, "jndi");
205 final URLStreamHandler handler
= new DirContextURLStreamHandler();
207 return bundleContext
.registerService(URLStreamHandlerService
.class.getName(),
208 new AbstractURLStreamHandlerService() {
210 private final static String EMPTY_STRING
= "";
213 public URLConnection
openConnection(URL u
) throws IOException
{
214 return new URL(u
, EMPTY_STRING
, handler
).openConnection();