]> git.argeo.org Git - lgpl/argeo-commons.git/blob - Activator.java
17ed5e996e1bafe308c3548f8f5ecaed8ec6711d
[lgpl/argeo-commons.git] / Activator.java
1 /*
2 * Copyright 2006-2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.osgi.web.tomcat.internal;
18
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.MalformedURLException;
24 import java.net.URL;
25 import java.net.URLConnection;
26 import java.net.URLStreamHandler;
27 import java.util.Properties;
28
29 import javax.management.MBeanRegistration;
30
31 import org.apache.catalina.Lifecycle;
32 import org.apache.catalina.Server;
33 import org.apache.catalina.Service;
34 import org.apache.catalina.connector.Connector;
35 import org.apache.catalina.core.StandardService;
36 import org.apache.catalina.util.ServerInfo;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.naming.resources.DirContextURLStreamHandler;
40 import org.argeo.catalina.start.CatalinaActivator;
41 import org.osgi.framework.Bundle;
42 import org.osgi.framework.BundleActivator;
43 import org.osgi.framework.BundleContext;
44 import org.osgi.framework.Constants;
45 import org.osgi.framework.ServiceRegistration;
46 import org.osgi.service.url.AbstractURLStreamHandlerService;
47 import org.osgi.service.url.URLConstants;
48 import org.osgi.service.url.URLStreamHandlerService;
49
50 /**
51 * Simple activator for starting Apache Tomcat Catalina container inside OSGi
52 * using Tomcat's XML configuration files.
53 *
54 * <p/>
55 * This activator looks initially for a <code>conf/server.xml</code> file
56 * falling back to <code>conf/default-server.xml</code>. This allows the default
57 * configuration to be tweaked through fragments for example.
58 *
59 * @author Costin Leau
60 */
61 public class Activator implements BundleActivator {
62
63 /** logger */
64 private static final Log log = LogFactory.getLog(Activator.class);
65
66 /** default XML configuration */
67 private static final String DEFAULT_XML_CONF_LOCATION = "conf/default-server.xml";
68
69 /** user-configurable XML configuration */
70 private static final String XML_CONF_LOCATION = "conf/server.xml";
71
72 private BundleContext bundleContext;
73
74 private StandardService server;
75
76 private ServiceRegistration registration, urlRegistration;
77
78 private Thread startupThread;
79
80 public void start(BundleContext context) throws Exception {
81 this.bundleContext = context;
82 // do the initialization on a different thread
83 // so the activator finishes fast
84 startupThread = new Thread(new Runnable() {
85
86 public void run() {
87 log.info("Starting " + ServerInfo.getServerInfo() + " ...");
88
89 // default startup procedure
90 ClassLoader cl = Activator.class.getClassLoader();
91 Thread current = Thread.currentThread();
92 ClassLoader old = current.getContextClassLoader();
93
94 try {
95 current.setContextClassLoader(cl);
96
97 server = createCatalinaServer(bundleContext.getBundle());
98
99 server.start();
100
101 Connector[] connectors = server.findConnectors();
102 for (int i = 0; i < connectors.length; i++) {
103 Connector conn = connectors[i];
104 log.info("Succesfully started "
105 + ServerInfo.getServerInfo() + " @ "
106 + conn.getDomain() + ":" + conn.getPort());
107 }
108
109 // register URL service
110 urlRegistration = registerTomcatJNDIUrlService();
111 // publish server as an OSGi service
112 registration = publishServerAsAService(server);
113 log.info("Published " + ServerInfo.getServerInfo()
114 + " as an OSGi service");
115 } catch (Exception ex) {
116 String msg = "Cannot start " + ServerInfo.getServerInfo();
117 log.error(msg, ex);
118 throw new RuntimeException(msg, ex);
119 } finally {
120 current.setContextClassLoader(old);
121 }
122 }
123 }, "Tomcat Catalina Start Thread");
124
125 startupThread.start();
126 }
127
128 public void stop(BundleContext context) throws Exception {
129 // unpublish service first
130 registration.unregister();
131 urlRegistration.unregister();
132
133 log.info("Unpublished " + ServerInfo.getServerInfo() + " OSGi service");
134
135 // default startup procedure
136 ClassLoader cl = Activator.class.getClassLoader();
137 Thread current = Thread.currentThread();
138 ClassLoader old = current.getContextClassLoader();
139
140 try {
141 current.setContextClassLoader(cl);
142 // reset CCL
143 // current.setContextClassLoader(null);
144 log.info("Stopping " + ServerInfo.getServerInfo() + " ...");
145 server.stop();
146 log.info("Succesfully stopped " + ServerInfo.getServerInfo());
147 } catch (Exception ex) {
148 log.error("Cannot stop " + ServerInfo.getServerInfo(), ex);
149 throw ex;
150 } finally {
151 current.setContextClassLoader(old);
152 }
153 }
154
155 private StandardService createCatalinaServer(Bundle bundle)
156 throws Exception {
157 URL xmlConfiguration = null;
158
159 if (System.getProperty(CatalinaActivator.ARGEO_SERVER_TOMCAT_CONFIG) != null) {
160 String customConfig = System
161 .getProperty(CatalinaActivator.ARGEO_SERVER_TOMCAT_CONFIG);
162 try {
163 xmlConfiguration = new URL(customConfig);
164 } catch (MalformedURLException e) {
165 // within this bundle
166 // typically 'default-server-ssl.xml'
167 xmlConfiguration = bundle.getResource(customConfig);
168 }
169 } else {
170 // fragment
171 xmlConfiguration = bundle.getResource(XML_CONF_LOCATION);
172 }
173
174 if (xmlConfiguration != null) {
175 log.info("Using custom XML configuration " + xmlConfiguration);
176 } else {
177 xmlConfiguration = bundle.getResource(DEFAULT_XML_CONF_LOCATION);
178 if (xmlConfiguration == null)
179 log.error("No XML configuration found; bailing out...");
180 else
181 log.info("Using default XML configuration " + xmlConfiguration);
182 }
183
184 return createServerFromXML(xmlConfiguration);
185 }
186
187 private StandardService createServerFromXML(URL xmlConfiguration)
188 throws IOException {
189 OsgiCatalina catalina = new OsgiCatalina();
190 catalina.setAwait(false);
191 catalina.setUseShutdownHook(false);
192 catalina.setName("Catalina");
193 catalina.setParentClassLoader(Thread.currentThread()
194 .getContextClassLoader());
195
196 // copy the URL file to a local temporary file (since Catalina doesn't
197 // use URL unfortunately)
198 File configTempFile = File.createTempFile("dm.catalina", ".cfg.xml");
199 configTempFile.deleteOnExit();
200
201 // copy URL to temporary file
202 copyURLToFile(xmlConfiguration.openStream(), new FileOutputStream(
203 configTempFile));
204 log.debug("Copied configuration " + xmlConfiguration
205 + " to temporary file " + configTempFile);
206
207 catalina.setConfigFile(configTempFile.getAbsolutePath());
208
209 catalina.load();
210
211 Server server = catalina.getServer();
212
213 return (StandardService) server.findServices()[0];
214 }
215
216 private void copyURLToFile(InputStream inStream, FileOutputStream outStream) {
217
218 int bytesRead;
219 byte[] buf = new byte[4096];
220 try {
221 while ((bytesRead = inStream.read(buf)) >= 0) {
222 outStream.write(buf, 0, bytesRead);
223 }
224 } catch (IOException ex) {
225 throw (RuntimeException) new IllegalStateException(
226 "Cannot copy URL to file").initCause(ex);
227 } finally {
228 try {
229 inStream.close();
230 } catch (IOException ignore) {
231 }
232 try {
233 outStream.close();
234 } catch (IOException ignore) {
235 }
236 }
237 }
238
239 private ServiceRegistration publishServerAsAService(StandardService server) {
240 Properties props = new Properties();
241 // put some extra properties to easily identify the service
242 props.put(Constants.SERVICE_VENDOR, "Spring Dynamic Modules");
243 props.put(Constants.SERVICE_DESCRIPTION, ServerInfo.getServerInfo());
244 props.put(Constants.BUNDLE_VERSION, ServerInfo.getServerNumber());
245 props.put(Constants.BUNDLE_NAME, bundleContext.getBundle()
246 .getSymbolicName());
247
248 // spring-dm specific property
249 props.put("org.springframework.osgi.bean.name", "tomcat-server");
250
251 // publish just the interfaces and the major classes
252 // (server/handlerWrapper)
253 String[] classes = new String[] { StandardService.class.getName(),
254 Service.class.getName(), MBeanRegistration.class.getName(),
255 Lifecycle.class.getName() };
256
257 return bundleContext.registerService(classes, server, props);
258 }
259
260 private ServiceRegistration registerTomcatJNDIUrlService() {
261 Properties properties = new Properties();
262 properties.put(URLConstants.URL_HANDLER_PROTOCOL, "jndi");
263 final URLStreamHandler handler = new DirContextURLStreamHandler();
264
265 return bundleContext.registerService(
266 URLStreamHandlerService.class.getName(),
267 new AbstractURLStreamHandlerService() {
268
269 private final static String EMPTY_STRING = "";
270
271 public URLConnection openConnection(URL u)
272 throws IOException {
273 return new URL(u, EMPTY_STRING, handler)
274 .openConnection();
275 }
276 }, properties);
277 }
278 }