View Javadoc

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   * Bootstrap jetty and publish a default Server instance as an OSGi service.
39   * 
40   * Listen for other Server instances to be published as services and support them as deployment targets.
41   * 
42   * Listen for Bundles to be activated, and deploy those that represent webapps to one of the known Server instances.
43   * 
44   * <ol>
45   * <li>basic servlet [ok]</li>
46   * <li>basic jetty.xml [ok]</li>
47   * <li>basic jetty.xml and jetty-plus.xml [ok]</li>
48   * <li>basic jsp [ok]</li>
49   * <li>jsp with tag-libs [ok]</li>
50   * <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
51   * </ul>
52   */
53  public class JettyBootstrapActivator implements BundleActivator
54  {
55  
56      private static JettyBootstrapActivator INSTANCE = null;
57  
58      public static JettyBootstrapActivator getInstance()
59      {
60          return INSTANCE;
61      }
62  
63      private ServiceRegistration _registeredServer;
64  
65      private Server _server;
66  
67      private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
68  
69      private PackageAdminServiceTracker _packageAdminServiceTracker;
70  
71      private BundleTracker _webBundleTracker;
72  
73      private BundleContext _bundleContext;
74  
75      private JettyServerServiceTracker _jettyServerServiceTracker;
76  
77      /**
78       * Setup a new jetty Server, registers it as a service. Setup the Service
79       * tracker for the jetty ContextHandlers that are in charge of deploying the
80       * webapps. Setup the BundleListener that supports the extender pattern for
81       * the jetty ContextHandler.
82       * 
83       * @param context
84       */
85      public void start(BundleContext context) throws Exception
86      {
87          INSTANCE = this;
88          _bundleContext = context;
89  
90          // track other bundles and fragments attached to this bundle that we
91          // should activate.
92          _packageAdminServiceTracker = new PackageAdminServiceTracker(context);
93  
94          // track Server instances that we should support as deployment targets
95          _jettyServerServiceTracker = new JettyServerServiceTracker();
96          context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
97  
98          // track ContextHandler class instances and deploy them to one of the known Servers
99          _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker(_jettyServerServiceTracker);
100         context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
101 
102         // Create a default jetty instance right now.
103         DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
104 
105         // track Bundles and deploy those that represent webapps to one of the known Servers
106         _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
107         _webBundleTracker.open();
108     }
109 
110     /**
111      * Stop the activator.
112      * 
113      * @see
114      * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
115      */
116     public void stop(BundleContext context) throws Exception
117     {
118         try
119         {
120 
121             if (_webBundleTracker != null)
122             {
123                 _webBundleTracker.close();
124                 _webBundleTracker = null;
125             }
126             if (_jettyContextHandlerTracker != null)
127             {
128                 _jettyContextHandlerTracker.stop();
129                 context.removeServiceListener(_jettyContextHandlerTracker);
130                 _jettyContextHandlerTracker = null;
131             }
132             if (_jettyServerServiceTracker != null)
133             {
134                 _jettyServerServiceTracker.stop();
135                 context.removeServiceListener(_jettyServerServiceTracker);
136                 _jettyServerServiceTracker = null;
137             }
138             if (_packageAdminServiceTracker != null)
139             {
140                 _packageAdminServiceTracker.stop();
141                 context.removeServiceListener(_packageAdminServiceTracker);
142                 _packageAdminServiceTracker = null;
143             }
144             if (_registeredServer != null)
145             {
146                 try
147                 {
148                     _registeredServer.unregister();
149                 }
150                 catch (IllegalArgumentException ill)
151                 {
152                     // already unregistered.
153                 }
154                 finally
155                 {
156                     _registeredServer = null;
157                 }
158             }
159         }
160         finally
161         {
162             if (_server != null)
163             {
164                 _server.stop();
165             }
166             INSTANCE = null;
167         }
168     }
169 
170     /**
171      * Helper method that creates a new org.jetty.webapp.WebAppContext and
172      * registers it as an OSGi service. The tracker
173      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
174      * 
175      * @param contributor The bundle
176      * @param webappFolderPath The path to the root of the webapp. Must be a
177      *            path relative to bundle; either an absolute path.
178      * @param contextPath The context path. Must start with "/"
179      * @throws Exception
180      */
181     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
182     {
183         checkBundleActivated();
184         WebAppContext contextHandler = new WebAppContext();
185         Dictionary<String,String> dic = new Hashtable<String,String>();
186         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
187         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
188         String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
189         if (requireTldBundle != null)
190         {
191             dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
192         }
193         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
194     }
195 
196     /**
197      * Helper method that creates a new org.jetty.webapp.WebAppContext and
198      * registers it as an OSGi service. The tracker
199      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
200      * 
201      * @param contributor The bundle
202      * @param webappFolderPath The path to the root of the webapp. Must be a
203      *            path relative to bundle; either an absolute path.
204      * @param contextPath The context path. Must start with "/"
205      * @param dic TODO: parameter description
206      * @throws Exception
207      */
208     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
209     {
210         checkBundleActivated();
211         WebAppContext contextHandler = new WebAppContext();
212         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
213         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
214         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
215     }
216 
217     /**
218      * Helper method that creates a new skeleton of a ContextHandler and
219      * registers it as an OSGi service. The tracker
220      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
221      * 
222      * @param contributor The bundle that registers a new context
223      * @param contextFilePath The path to the file inside the bundle that
224      *            defines the context.
225      * @throws Exception
226      */
227     public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
228     {
229         registerContext(contributor, contextFilePath, new Hashtable<String, String>());
230     }
231 
232     /**
233      * Helper method that creates a new skeleton of a ContextHandler and
234      * registers it as an OSGi service. The tracker
235      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
236      * 
237      * @param contributor The bundle that registers a new context
238      * @param contextFilePath The path to the file inside the bundle that
239      *            defines the context.
240      * @param dic TODO: parameter description
241      * @throws Exception
242      */
243     public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
244     {
245         checkBundleActivated();
246         ContextHandler contextHandler = new ContextHandler();
247         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
248         dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
249         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
250     }
251 
252     public static void unregister(String contextPath)
253     {
254         // todo
255     }
256 
257     /**
258      * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
259      * when one of the static methods to register a webapp is called we should
260      * make sure that the bundle is started.
261      */
262     private static void checkBundleActivated()
263     {
264         if (INSTANCE == null)
265         {
266             Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
267             try
268             {
269                 thisBundle.start();
270             }
271             catch (BundleException e)
272             {
273                 // nevermind.
274             }
275         }
276     }
277 
278     /**
279      * @return The bundle context for this bundle.
280      */
281     public static BundleContext getBundleContext()
282     {
283         checkBundleActivated();
284         return INSTANCE._bundleContext;
285     }
286 
287 }