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         _webBundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STOPPING, new WebBundleTrackerCustomizer());
109         _webBundleTracker.open();
110     }
111 
112     /**
113      * Stop the activator.
114      * 
115      * @see
116      * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
117      */
118     public void stop(BundleContext context) throws Exception
119     {
120         try
121         {
122 
123             if (_webBundleTracker != null)
124             {
125                 _webBundleTracker.close();
126                 _webBundleTracker = null;
127             }
128             if (_jettyContextHandlerTracker != null)
129             {
130                 context.removeServiceListener(_jettyContextHandlerTracker);
131                 _jettyContextHandlerTracker = null;
132             }
133             if (_jettyServerServiceTracker != null)
134             {
135                 _jettyServerServiceTracker.stop();
136                 context.removeServiceListener(_jettyServerServiceTracker);
137                 _jettyServerServiceTracker = null;
138             }
139             if (_packageAdminServiceTracker != null)
140             {
141                 _packageAdminServiceTracker.stop();
142                 context.removeServiceListener(_packageAdminServiceTracker);
143                 _packageAdminServiceTracker = null;
144             }
145             if (_registeredServer != null)
146             {
147                 try
148                 {
149                     _registeredServer.unregister();
150                 }
151                 catch (IllegalArgumentException ill)
152                 {
153                     // already unregistered.
154                 }
155                 finally
156                 {
157                     _registeredServer = null;
158                 }
159             }
160         }
161         finally
162         {
163             INSTANCE = null;
164         }
165     }
166 
167     /**
168      * Helper method that creates a new org.jetty.webapp.WebAppContext and
169      * registers it as an OSGi service. The tracker
170      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
171      * 
172      * @param contributor The bundle
173      * @param webappFolderPath The path to the root of the webapp. Must be a
174      *            path relative to bundle; either an absolute path.
175      * @param contextPath The context path. Must start with "/"
176      * @throws Exception
177      */
178     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath) throws Exception
179     {
180         checkBundleActivated();
181         WebAppContext contextHandler = new WebAppContext();
182         Dictionary<String,String> dic = new Hashtable<String,String>();
183         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
184         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
185         String requireTldBundle = (String) contributor.getHeaders().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE);
186         if (requireTldBundle != null)
187         {
188             dic.put(OSGiWebappConstants.SERVICE_PROP_REQUIRE_TLD_BUNDLE, requireTldBundle);
189         }
190         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
191     }
192 
193     /**
194      * Helper method that creates a new org.jetty.webapp.WebAppContext and
195      * registers it as an OSGi service. The tracker
196      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
197      * 
198      * @param contributor The bundle
199      * @param webappFolderPath The path to the root of the webapp. Must be a
200      *            path relative to bundle; either an absolute path.
201      * @param contextPath The context path. Must start with "/"
202      * @param dic TODO: parameter description
203      * @throws Exception
204      */
205     public static void registerWebapplication(Bundle contributor, String webappFolderPath, String contextPath, Dictionary<String, String> dic) throws Exception
206     {
207         checkBundleActivated();
208         WebAppContext contextHandler = new WebAppContext();
209         dic.put(OSGiWebappConstants.SERVICE_PROP_WAR, webappFolderPath);
210         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_PATH, contextPath);
211         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
212     }
213 
214     /**
215      * Helper method that creates a new skeleton of a ContextHandler and
216      * registers it as an OSGi service. The tracker
217      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
218      * 
219      * @param contributor The bundle that registers a new context
220      * @param contextFilePath The path to the file inside the bundle that
221      *            defines the context.
222      * @throws Exception
223      */
224     public static void registerContext(Bundle contributor, String contextFilePath) throws Exception
225     {
226         registerContext(contributor, contextFilePath, new Hashtable<String, String>());
227     }
228 
229     /**
230      * Helper method that creates a new skeleton of a ContextHandler and
231      * registers it as an OSGi service. The tracker
232      * {@link JettyContextHandlerServiceTracker} will do the actual deployment.
233      * 
234      * @param contributor The bundle that registers a new context
235      * @param contextFilePath The path to the file inside the bundle that
236      *            defines the context.
237      * @param dic TODO: parameter description
238      * @throws Exception
239      */
240     public static void registerContext(Bundle contributor, String contextFilePath, Dictionary<String, String> dic) throws Exception
241     {
242         checkBundleActivated();
243         ContextHandler contextHandler = new ContextHandler();
244         dic.put(OSGiWebappConstants.SERVICE_PROP_CONTEXT_FILE_PATH, contextFilePath);
245         dic.put(IWebBundleDeployerHelper.INTERNAL_SERVICE_PROP_UNKNOWN_CONTEXT_HANDLER_TYPE, Boolean.TRUE.toString());
246         contributor.getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, dic);
247     }
248 
249     public static void unregister(String contextPath)
250     {
251         // todo
252     }
253 
254     /**
255      * Since org.eclipse.jetty.osgi.boot does not have a lazy activation policy
256      * when one of the static methods to register a webapp is called we should
257      * make sure that the bundle is started.
258      */
259     private static void checkBundleActivated()
260     {
261         if (INSTANCE == null)
262         {
263             Bundle thisBundle = FrameworkUtil.getBundle(JettyBootstrapActivator.class);
264             try
265             {
266                 thisBundle.start();
267             }
268             catch (BundleException e)
269             {
270                 // nevermind.
271             }
272         }
273     }
274 
275     /**
276      * @return The bundle context for this bundle.
277      */
278     public static BundleContext getBundleContext()
279     {
280         checkBundleActivated();
281         return INSTANCE._bundleContext;
282     }
283 
284 }