1 // ========================================================================
2 // Copyright (c) 2009 Intalio, Inc.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // Contributors:
13 // Hugues Malphettes - initial API and implementation
14 // ========================================================================
15 package org.eclipse.jetty.osgi.boot;
16
17 import java.util.Dictionary;
18 import java.util.Hashtable;
19
20 import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
21 import org.eclipse.jetty.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
22 import org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper;
23 import org.eclipse.jetty.osgi.boot.internal.webapp.JettyContextHandlerServiceTracker;
24 import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
25 import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
26 import org.eclipse.jetty.server.Server;
27 import org.eclipse.jetty.server.handler.ContextHandler;
28 import org.eclipse.jetty.webapp.WebAppContext;
29 import org.osgi.framework.Bundle;
30 import org.osgi.framework.BundleActivator;
31 import org.osgi.framework.BundleContext;
32 import org.osgi.framework.BundleException;
33 import org.osgi.framework.FrameworkUtil;
34 import org.osgi.framework.ServiceRegistration;
35 import org.osgi.util.tracker.BundleTracker;
36
37 /**
38 * Experiment: bootstrap jetty's complete distrib from an OSGi bundle. Progress:
39 * <ol>
40 * <li>basic servlet [ok]</li>
41 * <li>basic jetty.xml [ok]</li>
42 * <li>basic jetty.xml and jetty-plus.xml [ok]</li>
43 * <li>basic jsp [ok with modifications]
44 * <ul>
45 * <li>Needed to modify the headers of jdt.core-3.1.1 so that its dependency on
46 * eclipse.runtime, eclipse.resources and eclipse.text are optional. Also we
47 * should depend on the latest jdt.core from eclipse-3.5 not from eclipse-3.1.1
48 * although that will require actual changes to jasper as some internal APIs of
49 * jdt.core have changed.</li>
50 * <li>Modifications to org.mortbay.jetty.jsp-2.1-glassfish: made all imports to
51 * ant, xalan and sun packages optional.</li>
52 * </ul>
53 * </li>
54 * <li>jsp with tag-libs [ok]</li>
55 * <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
56 * </ul>
57 */
58 public class JettyBootstrapActivator implements BundleActivator
59 {
60
61 private static JettyBootstrapActivator INSTANCE = null;
62
63 public static JettyBootstrapActivator getInstance()
64 {
65 return INSTANCE;
66 }
67
68 private ServiceRegistration _registeredServer;
69
70 private Server _server;
71
72 private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
73
74 private PackageAdminServiceTracker _packageAdminServiceTracker;
75
76 private BundleTracker _webBundleTracker;
77
78 private BundleContext _bundleContext;
79
80 // private ServiceRegistration _jettyServerFactoryService;
81 private JettyServerServiceTracker _jettyServerServiceTracker;
82
83 /**
84 * Setup a new jetty Server, registers it as a service. Setup the Service
85 * tracker for the jetty ContextHandlers that are in charge of deploying the
86 * webapps. Setup the BundleListener that supports the extender pattern for
87 * the jetty ContextHandler.
88 *
89 * @param context
90 */
91 public void start(BundleContext context) throws Exception
92 {
93 INSTANCE = this;
94 _bundleContext = context;
95
96 // track other bundles and fragments attached to this bundle that we
97 // should activate.
98 _packageAdminServiceTracker = new PackageAdminServiceTracker(context);
99
100 _jettyServerServiceTracker = new JettyServerServiceTracker();
101 context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
102
103 // Register the Jetty Server Factory as a ManagedServiceFactory:
104 // Properties jettyServerMgdFactoryServiceProps = new Properties();
105 // jettyServerMgdFactoryServiceProps.put("pid",
106 // OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
107 // _jettyServerFactoryService = context.registerService(
108 // ManagedServiceFactory.class.getName(), new
109 // JettyServersManagedFactory(),
110 // jettyServerMgdFactoryServiceProps);
111
112 _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
113
114 // the tracker in charge of the actual deployment
115 // and that will configure and start the jetty server.
116 context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
117
118 // see if we shoult start a default jetty instance right now.
119 DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
120
121 // now ready to support the Extender pattern:
122 _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
123 _webBundleTracker.open();
124
125 }
126
127 /*
128 * (non-Javadoc)
129 *
130 * @see
131 * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
132 */
133 public void stop(BundleContext context) throws Exception
134 {
135 try
136 {
137
138 if (_webBundleTracker != null)
139 {
140 _webBundleTracker.close();
141 _webBundleTracker = null;
142 }
143 if (_jettyContextHandlerTracker != null)
144 {
145 _jettyContextHandlerTracker.stop();
146 context.removeServiceListener(_jettyContextHandlerTracker);
147 _jettyContextHandlerTracker = null;
148 }
149 if (_jettyServerServiceTracker != null)
150 {
151 _jettyServerServiceTracker.stop();
152 context.removeServiceListener(_jettyServerServiceTracker);
153 _jettyServerServiceTracker = null;
154 }
155 if (_packageAdminServiceTracker != null)
156 {
157 _packageAdminServiceTracker.stop();
158 context.removeServiceListener(_packageAdminServiceTracker);
159 _packageAdminServiceTracker = null;
160 }
161 if (_registeredServer != null)
162 {
163 try
164 {
165 _registeredServer.unregister();
166 }
167 catch (IllegalArgumentException ill)
168 {
169 // already unregistered.
170 }
171 finally
172 {
173 _registeredServer = null;
174 }
175 }
176 // if (_jettyServerFactoryService != null)
177 // {
178 // try
179 // {
180 // _jettyServerFactoryService.unregister();
181 // }
182 // catch (IllegalArgumentException ill)
183 // {
184 // // already unregistered.
185 // }
186 // finally
187 // {
188 // _jettyServerFactoryService = null;
189 // }
190 // }
191
192 }
193 finally
194 {
195 if (_server != null)
196 {
197 _server.stop();
198 }
199 INSTANCE = null;
200 }
201 }
202
203 /**
204 * Helper method that creates a new org.jetty.webapp.WebAppContext and
205 * registers it as an OSGi service. The tracker
206 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
207 *
208 * @param contributor The bundle
209 * @param webappFolderPath The path to the root of the webapp. Must be a
210 * path relative to bundle; either an absolute path.
211 * @param contextPath The context path. Must start with "/"
212 * @throws Exception
213 */
214 public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
215 {
216 checkBundleActivated();
217 WebAppContext contextHandler = new WebAppContext();
218 Dictionary dic = new Hashtable();
219 dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
220 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
221 String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
222 if (requireTldBundle != null)
223 {
224 dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
225 }
226 contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
227 }
228
229 /**
230 * Helper method that creates a new org.jetty.webapp.WebAppContext and
231 * registers it as an OSGi service. The tracker
232 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
233 *
234 * @param contributor The bundle
235 * @param webappFolderPath The path to the root of the webapp. Must be a
236 * path relative to bundle; either an absolute path.
237 * @param contextPath The context path. Must start with "/"
238 * @param dic TODO: parameter description
239 * @throws Exception
240 */
241 public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
242 {
243 checkBundleActivated();
244 WebAppContext contextHandler = new WebAppContext();
245 dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
246 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
247 contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
248 }
249
250 /**
251 * Helper method that creates a new skeleton of a ContextHandler and
252 * registers it as an OSGi service. The tracker
253 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
254 *
255 * @param contributor The bundle that registers a new context
256 * @param contextFilePath The path to the file inside the bundle that
257 * defines the context.
258 * @throws Exception
259 */
260 public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
261 {
262 registerContext(contributor, contextFilePath, new Hashtable<String, String>());
263 }
264
265 /**
266 * Helper method that creates a new skeleton of a ContextHandler and
267 * registers it as an OSGi service. The tracker
268 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
269 *
270 * @param contributor The bundle that registers a new context
271 * @param contextFilePath The path to the file inside the bundle that
272 * defines the context.
273 * @param dic TODO: parameter description
274 * @throws Exception
275 */
276 public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
277 {
278 checkBundleActivated();
279 ContextHandler contextHandler = new ContextHandler();
280 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
281 dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
282 contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
283 }
284
285 public static void unregister(String contextPath)
286 {
287 // todo
288 }
289
290 /**
291 * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
292 * when one fo the static methods to register a webapp is called we should
293 * make sure that the bundle is started.
294 */
295 private static void checkBundleActivated()
296 {
297 if (INSTANCE == null)
298 {
299 Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
300 try
301 {
302 thisBundle.start();
303 }
304 catch (BundleException e)
305 {
306 // nevermind.
307 }
308 }
309 }
310
311 /**
312 * @return The bundle context for this bundle.
313 */
314 public static BundleContext getBundleContext()
315 {
316 checkBundleActivated();
317 return INSTANCE._bundleContext;
318 }
319
320 }