]> git.argeo.org Git - lgpl/argeo-commons.git/blob - server/runtime/org.argeo.server.catalina/src/main/java/org/argeo/server/catalina/CatalinaServer.java
Fix build order
[lgpl/argeo-commons.git] / server / runtime / org.argeo.server.catalina / src / main / java / org / argeo / server / catalina / CatalinaServer.java
1 package org.argeo.server.catalina;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.net.URL;
8 import java.net.URLConnection;
9 import java.net.URLStreamHandler;
10 import java.util.Properties;
11
12 import javax.management.MBeanRegistration;
13
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;
32
33 public class CatalinaServer implements DisposableBean,InitializingBean{
34 /** logger */
35 private static final Log log = LogFactory.getLog(CatalinaServer.class);
36
37 /** default XML configuration */
38 private static final String DEFAULT_XML_CONF_LOCATION = "conf/default-server.xml";
39
40 /** user-configurable XML configuration */
41 private static final String XML_CONF_LOCATION = "conf/server.xml";
42
43 private BundleContext bundleContext;
44
45 private StandardService server;
46
47 private ServiceRegistration registration, urlRegistration;
48
49 public void afterPropertiesSet() throws Exception {
50 log.info("Starting " + ServerInfo.getServerInfo() + " ...");
51
52 // default startup procedure
53 ClassLoader cl = CatalinaServer.class.getClassLoader();
54 Thread current = Thread.currentThread();
55 ClassLoader old = current.getContextClassLoader();
56
57 try {
58 current.setContextClassLoader(cl);
59
60 server = createCatalinaServer(bundleContext.getBundle());
61
62 server.start();
63
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() + ":"
68 + conn.getPort());
69 }
70
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");
76 }
77 catch (Exception ex) {
78 String msg = "Cannot start " + ServerInfo.getServerInfo();
79 log.error(msg, ex);
80 throw new RuntimeException(msg, ex);
81 }
82 finally {
83 current.setContextClassLoader(old);
84 }
85 }
86
87 public void destroy() throws Exception {
88 // unpublish service first
89 registration.unregister();
90 urlRegistration.unregister();
91
92 log.info("Unpublished " + ServerInfo.getServerInfo() + " OSGi service");
93
94 // default startup procedure
95 ClassLoader cl = CatalinaServer.class.getClassLoader();
96 Thread current = Thread.currentThread();
97 ClassLoader old = current.getContextClassLoader();
98
99 try {
100 current.setContextClassLoader(cl);
101 //reset CCL
102 // current.setContextClassLoader(null);
103 log.info("Stopping " + ServerInfo.getServerInfo() + " ...");
104 server.stop();
105 log.info("Succesfully stopped " + ServerInfo.getServerInfo());
106 }
107 catch (Exception ex) {
108 log.error("Cannot stop " + ServerInfo.getServerInfo(), ex);
109 throw ex;
110 }
111 finally {
112 current.setContextClassLoader(old);
113 }
114 }
115
116 private StandardService createCatalinaServer(Bundle bundle) throws Exception {
117 // first try to use the XML file
118 URL xmlConfiguration = bundle.getResource(XML_CONF_LOCATION);
119
120 if (xmlConfiguration != null) {
121 log.info("Using custom XML configuration " + xmlConfiguration);
122 }
123 else {
124 xmlConfiguration = bundle.getResource(DEFAULT_XML_CONF_LOCATION);
125 if (xmlConfiguration == null)
126 log.error("No XML configuration found; bailing out...");
127 else
128 log.info("Using default XML configuration " + xmlConfiguration);
129 }
130
131 return createServerFromXML(xmlConfiguration);
132 }
133
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());
140
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();
144
145 // copy URL to temporary file
146 copyURLToFile(xmlConfiguration.openStream(), new FileOutputStream(configTempFile));
147 log.debug("Copied configuration " + xmlConfiguration + " to temporary file " + configTempFile);
148
149 catalina.setConfigFile(configTempFile.getAbsolutePath());
150
151 catalina.load();
152
153 Server server = catalina.getServer();
154
155 return (StandardService) server.findServices()[0];
156 }
157
158 private void copyURLToFile(InputStream inStream, FileOutputStream outStream) {
159
160 int bytesRead;
161 byte[] buf = new byte[4096];
162 try {
163 while ((bytesRead = inStream.read(buf)) >= 0) {
164 outStream.write(buf, 0, bytesRead);
165 }
166 }
167 catch (IOException ex) {
168 throw (RuntimeException) new IllegalStateException("Cannot copy URL to file").initCause(ex);
169 }
170 finally {
171 try {
172 inStream.close();
173 }
174 catch (IOException ignore) {
175 }
176 try {
177 outStream.close();
178 }
179 catch (IOException ignore) {
180 }
181 }
182 }
183
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());
191
192 // spring-dm specific property
193 props.put("org.springframework.osgi.bean.name", "tomcat-server");
194
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() };
198
199 return bundleContext.registerService(classes, server, props);
200 }
201
202 private ServiceRegistration registerTomcatJNDIUrlService() {
203 Properties properties = new Properties();
204 properties.put(URLConstants.URL_HANDLER_PROTOCOL, "jndi");
205 final URLStreamHandler handler = new DirContextURLStreamHandler();
206
207 return bundleContext.registerService(URLStreamHandlerService.class.getName(),
208 new AbstractURLStreamHandlerService() {
209
210 private final static String EMPTY_STRING = "";
211
212
213 public URLConnection openConnection(URL u) throws IOException {
214 return new URL(u, EMPTY_STRING, handler).openConnection();
215 }
216 }, properties);
217 }
218
219 }