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 private Server _server;
70 private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
71 private PackageAdminServiceTracker _packageAdminServiceTracker;
72 private BundleTracker _webBundleTracker;
73 private BundleContext _bundleContext;
74
75 // private ServiceRegistration _jettyServerFactoryService;
76 private JettyServerServiceTracker _jettyServerServiceTracker;
77
78
79 /**
80 * Setup a new jetty Server, registers it as a service. Setup the Service
81 * tracker for the jetty ContextHandlers that are in charge of deploying the
82 * webapps. Setup the BundleListener that supports the extender pattern for
83 * the jetty ContextHandler.
84 *
85 * @param context
86 */
87 public void start(BundleContext context) throws Exception
88 {
89 INSTANCE = this;
90 _bundleContext = context;
91
92 // track other bundles and fragments attached to this bundle that we
93 // should activate.
94 _packageAdminServiceTracker = new PackageAdminServiceTracker(context);
95
96 _jettyServerServiceTracker = new JettyServerServiceTracker();
97 context.addServiceListener(_jettyServerServiceTracker,"(objectclass=" + Server.class.getName() + ")");
98
99 //Register the Jetty Server Factory as a ManagedServiceFactory:
100 // Properties jettyServerMgdFactoryServiceProps = new Properties();
101 // jettyServerMgdFactoryServiceProps.put("pid", OSGiWebappConstants.MANAGED_JETTY_SERVER_FACTORY_PID);
102 // _jettyServerFactoryService = context.registerService(
103 // ManagedServiceFactory.class.getName(), new JettyServersManagedFactory(),
104 // jettyServerMgdFactoryServiceProps);
105
106 _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
107
108 // the tracker in charge of the actual deployment
109 // and that will configure and start the jetty server.
110 context.addServiceListener(_jettyContextHandlerTracker,"(objectclass=" + ContextHandler.class.getName() + ")");
111
112 //see if we shoult start a default jetty instance right now.
113 DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
114
115 // now ready to support the Extender pattern:
116 _webBundleTracker = new BundleTracker(context,
117 Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
118 _webBundleTracker.open();
119
120 }
121
122 /*
123 * (non-Javadoc)
124 *
125 * @see
126 * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
127 */
128 public void stop(BundleContext context) throws Exception
129 {
130 try
131 {
132
133 if (_webBundleTracker != null)
134 {
135 _webBundleTracker.close();
136 _webBundleTracker = null;
137 }
138 if (_jettyContextHandlerTracker != null)
139 {
140 _jettyContextHandlerTracker.stop();
141 context.removeServiceListener(_jettyContextHandlerTracker);
142 _jettyContextHandlerTracker = null;
143 }
144 if (_jettyServerServiceTracker != null)
145 {
146 _jettyServerServiceTracker.stop();
147 context.removeServiceListener(_jettyServerServiceTracker);
148 _jettyServerServiceTracker = null;
149 }
150 if (_packageAdminServiceTracker != null)
151 {
152 _packageAdminServiceTracker.stop();
153 context.removeServiceListener(_packageAdminServiceTracker);
154 _packageAdminServiceTracker = null;
155 }
156 if (_registeredServer != null)
157 {
158 try
159 {
160 _registeredServer.unregister();
161 }
162 catch (IllegalArgumentException ill)
163 {
164 // already unregistered.
165 }
166 finally
167 {
168 _registeredServer = null;
169 }
170 }
171 // if (_jettyServerFactoryService != null)
172 // {
173 // try
174 // {
175 // _jettyServerFactoryService.unregister();
176 // }
177 // catch (IllegalArgumentException ill)
178 // {
179 // // already unregistered.
180 // }
181 // finally
182 // {
183 // _jettyServerFactoryService = null;
184 // }
185 // }
186
187 }
188 finally
189 {
190 if (_server != null)
191 {
192 _server.stop();
193 }
194 INSTANCE = null;
195 }
196 }
197
198 /**
199 * Helper method that creates a new org.jetty.webapp.WebAppContext and
200 * registers it as an OSGi service. The tracker
201 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
202 *
203 * @param contributor
204 * The bundle
205 * @param webappFolderPath
206 * The path to the root of the webapp. Must be a path relative to
207 * bundle; either an absolute path.
208 * @param contextPath
209 * The context path. Must start with "/"
210 * @throws Exception
211 */
212 public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
213 {
214 checkBundleActivated();
215 WebAppContext contextHandler = new WebAppContext();
216 Dictionary dic = new Hashtable();
217 dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath);
218 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath);
219 String requireTldBundle = (String)contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
220 if (requireTldBundle != null) {
221 dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
222 }
223 contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic);
224 }
225
226 /**
227 * Helper method that creates a new org.jetty.webapp.WebAppContext and
228 * registers it as an OSGi service. The tracker
229 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
230 *
231 * @param contributor
232 * The bundle
233 * @param webappFolderPath
234 * The path to the root of the webapp. Must be a path relative to
235 * bundle; either an absolute path.
236 * @param contextPath
237 * The context path. Must start with "/"
238 * @param dic
239 * TODO: parameter description
240 * @throws Exception
241 */
242 public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
243 {
244 checkBundleActivated();
245 WebAppContext contextHandler = new WebAppContext();
246 dic.put(OSGiWebappConstants.SERVICE_PROP_WAR,webappFolderPath);
247 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH,contextPath);
248 contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic);
249 }
250
251 /**
252 * Helper method that creates a new skeleton of a ContextHandler and
253 * registers it as an OSGi service. The tracker
254 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
255 *
256 * @param contributor
257 * The bundle that registers a new context
258 * @param contextFilePath
259 * The path to the file inside the bundle that defines the
260 * context.
261 * @throws Exception
262 */
263 public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
264 {
265 registerContext(contributor,contextFilePath,new Hashtable<String, String>());
266 }
267
268 /**
269 * Helper method that creates a new skeleton of a ContextHandler and
270 * registers it as an OSGi service. The tracker
271 * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
272 *
273 * @param contributor
274 * The bundle that registers a new context
275 * @param contextFilePath
276 * The path to the file inside the bundle that defines the
277 * context.
278 * @param dic
279 * TODO: parameter description
280 * @throws Exception
281 */
282 public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
283 {
284 checkBundleActivated();
285 ContextHandler contextHandler = new ContextHandler();
286 dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH,contextFilePath);
287 dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE,Boolean.TRUE.toString());
288 contributor.getBundleContext().registerService(ContextHandler.class.getName(),contextHandler,dic);
289 }
290
291 public static void unregister(String contextPath)
292 {
293 // todo
294 }
295
296 /**
297 * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
298 * when one fo the static methods to register a webapp is called we should make sure that
299 * the bundle is started.
300 */
301 private static void checkBundleActivated()
302 {
303 if (INSTANCE == null)
304 {
305 Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
306 try
307 {
308 thisBundle.start();
309 }
310 catch (BundleException e)
311 {
312 // nevermind.
313 }
314 }
315 }
316
317 /**
318 * @return The bundle context for this bundle.
319 */
320 public static BundleContext getBundleContext()
321 {
322 checkBundleActivated();
323 return INSTANCE._bundleContext;
324 }
325
326
327 }