View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.osgi.boot;
20  
21  import java.util.Dictionary;
22  import java.util.Hashtable;
23  
24  import org.eclipse.jetty.osgi.boot.internal.serverfactory.DefaultJettyAtJettyHomeHelper;
25  import org.eclipse.jetty.osgi.boot.internal.serverfactory.JettyServerServiceTracker;
26  import org.eclipse.jetty.osgi.boot.internal.webapp.IWebBundleDeployerHelper;
27  import org.eclipse.jetty.osgi.boot.internal.webapp.JettyContextHandlerServiceTracker;
28  import org.eclipse.jetty.osgi.boot.internal.webapp.WebBundleTrackerCustomizer;
29  import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker;
30  import org.eclipse.jetty.server.Server;
31  import org.eclipse.jetty.server.handler.ContextHandler;
32  import org.eclipse.jetty.webapp.WebAppContext;
33  import org.osgi.framework.Bundle;
34  import org.osgi.framework.BundleActivator;
35  import org.osgi.framework.BundleContext;
36  import org.osgi.framework.BundleException;
37  import org.osgi.framework.FrameworkUtil;
38  import org.osgi.framework.ServiceRegistration;
39  import org.osgi.util.tracker.BundleTracker;
40  
41  /**
42   * Bootstrap jetty and publish a default Server instance as an OSGi service.
43   * 
44   * Listen for other Server instances to be published as services and support them as deployment targets.
45   * 
46   * Listen for Bundles to be activated, and deploy those that represent webapps to one of the known Server instances.
47   * 
48   * <ol>
49   * <li>basic servlet [ok]</li>
50   * <li>basic jetty.xml [ok]</li>
51   * <li>basic jetty.xml and jetty-plus.xml [ok]</li>
52   * <li>basic jsp [ok]</li>
53   * <li>jsp with tag-libs [ok]</li>
54   * <li>test-jndi with atomikos and derby inside ${jetty.home}/lib/ext [ok]</li>
55   * </ul>
56   */
57  public class JettyBootstrapActivator implements BundleActivator
58  {
59  
60      private static JettyBootstrapActivator INSTANCE = null;
61  
62      public static JettyBootstrapActivator getInstance()
63      {
64          return INSTANCE;
65      }
66  
67      private ServiceRegistration _registeredServer;
68  
69      private JettyContextHandlerServiceTracker _jettyContextHandlerTracker;
70  
71      private PackageAdminServiceTracker _packageAdminServiceTracker;
72  
73      private BundleTracker _webBundleTracker;
74  
75      private BundleContext _bundleContext;
76  
77      private JettyServerServiceTracker _jettyServerServiceTracker;
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          // track jetty Server instances that we should support as deployment targets
97          _jettyServerServiceTracker = new JettyServerServiceTracker();
98          context.addServiceListener(_jettyServerServiceTracker, "(objectclass=" + Server.class.getName() + ")");
99  
100         // track ContextHandler class instances and deploy them to one of the known Servers
101         _jettyContextHandlerTracker = new JettyContextHandlerServiceTracker();
102         context.addServiceListener(_jettyContextHandlerTracker, "(objectclass=" + ContextHandler.class.getName() + ")");
103 
104         // Create a default jetty instance right now.
105         DefaultJettyAtJettyHomeHelper.startJettyAtJettyHome(context);
106 
107         // track Bundles and deploy those that represent webapps to one of the known Servers
108         WebBundleTrackerCustomizer customizer = new WebBundleTrackerCustomizer();
109         _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, customizer);
110         customizer.setAndOpenWebBundleTracker(_webBundleTracker);
111     }
112 
113     /**
114      * Stop the activator.
115      * 
116      * @see
117      * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
118      */
119     public void stop(BundleContext context) throws Exception
120     {
121         try
122         {
123 
124             if (_webBundleTracker != null)
125             {
126                 _webBundleTracker.close();
127                 _webBundleTracker = null;
128             }
129             if (_jettyContextHandlerTracker != null)
130             {
131                 context.removeServiceListener(_jettyContextHandlerTracker);
132                 _jettyContextHandlerTracker = null;
133             }
134             if (_jettyServerServiceTracker != null)
135             {
136                 _jettyServerServiceTracker.stop();
137                 context.removeServiceListener(_jettyServerServiceTracker);
138                 _jettyServerServiceTracker = null;
139             }
140             if (_packageAdminServiceTracker != null)
141             {
142                 _packageAdminServiceTracker.stop();
143                 context.removeServiceListener(_packageAdminServiceTracker);
144                 _packageAdminServiceTracker = null;
145             }
146             if (_registeredServer != null)
147             {
148                 try
149                 {
150                     _registeredServer.unregister();
151                 }
152                 catch (IllegalArgumentException ill)
153                 {
154                     // already unregistered.
155                 }
156                 finally
157                 {
158                     _registeredServer = null;
159                 }
160             }
161         }
162         finally
163         {
164             INSTANCE = null;
165         }
166     }
167 
168     /**
169      * Helper method that creates a new org.jetty.webapp.WebAppContext and
170      * registers it as an OSGi service. The tracker
171      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
172      * 
173      * @param contributor The bundle
174      * @param webappFolderPath The path to the root of the webapp. Must be a
175      *            path relative to bundle; either an absolute path.
176      * @param contextPath The context path. Must start with "/"
177      * @throws Exception
178      */
179     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
180     {
181         checkBundleActivated();
182         WebAppContext contextHandler = new WebAppContext();
183         Dictionary<String,String> dic = new Hashtable<String,String>();
184         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
185         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
186         String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
187         if (requireTldBundle != null)
188         {
189             dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
190         }
191         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
192     }
193 
194     /**
195      * Helper method that creates a new org.jetty.webapp.WebAppContext and
196      * registers it as an OSGi service. The tracker
197      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
198      * 
199      * @param contributor The bundle
200      * @param webappFolderPath The path to the root of the webapp. Must be a
201      *            path relative to bundle; either an absolute path.
202      * @param contextPath The context path. Must start with "/"
203      * @param dic TODO: parameter description
204      * @throws Exception
205      */
206     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
207     {
208         checkBundleActivated();
209         WebAppContext contextHandler = new WebAppContext();
210         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
211         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
212         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
213     }
214 
215     /**
216      * Helper method that creates a new skeleton of a ContextHandler and
217      * registers it as an OSGi service. The tracker
218      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
219      * 
220      * @param contributor The bundle that registers a new context
221      * @param contextFilePath The path to the file inside the bundle that
222      *            defines the context.
223      * @throws Exception
224      */
225     public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
226     {
227         registerContext(contributor, contextFilePath, new Hashtable<String, String>());
228     }
229 
230     /**
231      * Helper method that creates a new skeleton of a ContextHandler and
232      * registers it as an OSGi service. The tracker
233      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
234      * 
235      * @param contributor The bundle that registers a new context
236      * @param contextFilePath The path to the file inside the bundle that
237      *            defines the context.
238      * @param dic TODO: parameter description
239      * @throws Exception
240      */
241     public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
242     {
243         checkBundleActivated();
244         ContextHandler contextHandler = new ContextHandler();
245         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
246         dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
247         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
248     }
249 
250     public static void unregister(String contextPath)
251     {
252         // todo
253     }
254 
255     /**
256      * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
257      * when one of the static methods to register a webapp is called we should
258      * make sure that the bundle is started.
259      */
260     private static void checkBundleActivated()
261     {
262         if (INSTANCE == null)
263         {
264             Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
265             try
266             {
267                 thisBundle.start();
268             }
269             catch (BundleException e)
270             {
271                 // nevermind.
272             }
273         }
274     }
275 
276     /**
277      * @return The bundle context for this bundle.
278      */
279     public static BundleContext getBundleContext()
280     {
281         checkBundleActivated();
282         return INSTANCE._bundleContext;
283     }
284 
285 }