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.internal.webapp;
20  
21  
22  import java.util.ArrayList;
23  import java.util.Collection;
24  
25  import org.eclipse.jetty.osgi.boot.BundleProvider;
26  import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
27  import org.eclipse.jetty.osgi.boot.utils.WebappRegistrationCustomizer;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  import org.osgi.framework.Bundle;
31  import org.osgi.framework.BundleEvent;
32  import org.osgi.framework.FrameworkUtil;
33  import org.osgi.framework.ServiceReference;
34  import org.osgi.util.tracker.BundleTracker;
35  import org.osgi.util.tracker.BundleTrackerCustomizer;
36  import org.osgi.util.tracker.ServiceTracker;
37  
38  /**
39   * WebBundleTrackerCustomizer
40   * 
41   * 
42   * Support bundles that declare a webpp or context directly through headers in their
43   * manifest. They will be deployed to the default jetty Server instance.
44   * 
45   * If you wish to deploy a context or webapp to a different jetty Server instance,
46   * register your context/webapp as an osgi service, and set the property OSGiServerConstants.MANAGED_JETTY_SERVER_NAME
47   * with the name of the Server instance you wish to depoy to.
48   * 
49   * @author hmalphettes
50   */
51  public class WebBundleTrackerCustomizer implements BundleTrackerCustomizer
52  {
53      private static final Logger LOG = Log.getLogger(WebBundleTrackerCustomizer.class);
54      
55      public static Collection<WebappRegistrationCustomizer> JSP_REGISTRATION_HELPERS = new ArrayList<WebappRegistrationCustomizer>();
56      public static final String FILTER = "(&(objectclass=" + BundleProvider.class.getName() + ")"+
57                                            "("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
58  
59      private ServiceTracker _serviceTracker;
60      private BundleTracker _bundleTracker;
61      
62      /* ------------------------------------------------------------ */
63      /**
64       * @throws Exception
65       */
66      public WebBundleTrackerCustomizer ()
67      throws Exception
68      {
69          Bundle myBundle = FrameworkUtil.getBundle(this.getClass());
70          
71          //track all instances of deployers of webapps/contexts as bundles       
72          _serviceTracker = new ServiceTracker(myBundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null) {
73              public Object addingService(ServiceReference reference) {
74                  Object object = super.addingService(reference);
75                  LOG.debug("Deployer registered {}", reference);
76                  openBundleTracker();
77                  return object;
78              }
79          };
80          _serviceTracker.open();
81  
82      }
83      
84      
85      /* ------------------------------------------------------------ */
86      /**
87       * A bundle is being added to the <code>BundleTracker</code>.
88       * 
89       * <p>
90       * This method is called before a bundle which matched the search parameters
91       * of the <code>BundleTracker</code> is added to the
92       * <code>BundleTracker</code>. This method should return the object to be
93       * tracked for the specified <code>Bundle</code>. The returned object is
94       * stored in the <code>BundleTracker</code> and is available from the
95       * {@link BundleTracker#getObject(Bundle) getObject} method.
96       * 
97       * @param bundle The <code>Bundle</code> being added to the
98       *            <code>BundleTracker</code>.
99       * @param event The bundle event which caused this customizer method to be
100      *            called or <code>null</code> if there is no bundle event
101      *            associated with the call to this method.
102      * @return The object to be tracked for the specified <code>Bundle</code>
103      *         object or <code>null</code> if the specified <code>Bundle</code>
104      *         object should not be tracked.
105      */
106     public Object addingBundle(Bundle bundle, BundleEvent event)
107     {
108         if (bundle.getState() == Bundle.ACTIVE)
109         {
110             register(bundle);          
111         }
112         else if (bundle.getState() == Bundle.STOPPING)
113         {
114             unregister(bundle);
115         }
116         else
117         {
118             // we should not be called in that state as
119             // we are registered only for ACTIVE and STOPPING
120         }
121         return null;
122     }
123 
124     
125     /* ------------------------------------------------------------ */
126     /**
127      * A bundle tracked by the <code>BundleTracker</code> has been modified.
128      * 
129      * <p>
130      * This method is called when a bundle being tracked by the
131      * <code>BundleTracker</code> has had its state modified.
132      * 
133      * @param bundle The <code>Bundle</code> whose state has been modified.
134      * @param event The bundle event which caused this customizer method to be
135      *            called or <code>null</code> if there is no bundle event
136      *            associated with the call to this method.
137      * @param object The tracked object for the specified bundle.
138      */
139     public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
140     {
141         // nothing the web-bundle was already track. something changed.
142         // we only reload the webapps if the bundle is stopped and restarted.
143         if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
144         {
145             unregister(bundle);
146         }
147         if (bundle.getState() == Bundle.ACTIVE)
148         {
149             register(bundle);
150         }
151     }
152 
153     
154     /* ------------------------------------------------------------ */
155     /**
156      * A bundle tracked by the <code>BundleTracker</code> has been removed.
157      * 
158      * <p>
159      * This method is called after a bundle is no longer being tracked by the
160      * <code>BundleTracker</code>.
161      * 
162      * @param bundle The <code>Bundle</code> that has been removed.
163      * @param event The bundle event which caused this customizer method to be
164      *            called or <code>null</code> if there is no bundle event
165      *            associated with the call to this method.
166      * @param object The tracked object for the specified bundle.
167      */
168     public void removedBundle(Bundle bundle, BundleEvent event, Object object)
169     {
170         unregister(bundle);
171     }
172 
173     
174     /* ------------------------------------------------------------ */
175     /**
176      * @param bundle
177      * @return true if this bundle in indeed a web-bundle.
178      */
179     private boolean register(Bundle bundle)
180     {
181         if (bundle == null)
182             return false;
183 
184         //It might be a bundle that we can deploy to our default jetty server instance
185         boolean deployed = false;
186         Object[] deployers = _serviceTracker.getServices();
187         if (deployers != null)
188         {
189             int i=0;
190             while (!deployed && i<deployers.length)
191             {
192 
193                 BundleProvider p = (BundleProvider)deployers[i];
194                 try
195                 {
196                     deployed = p.bundleAdded(bundle);
197                 }
198                 catch (Exception x)
199                 {
200                     LOG.warn("Error deploying bundle for jetty context", x);
201                 }
202                 i++;
203             }
204         }
205 
206         return deployed;
207     }
208 
209     /* ------------------------------------------------------------ */
210     /**
211      * @param bundle
212      */
213     private void unregister(Bundle bundle)
214     { 
215         Object[] deployers = _serviceTracker.getServices();
216         boolean undeployed = false;
217         if (deployers != null)
218         {
219             int i=0;
220             while (!undeployed && i<deployers.length)
221             {
222                 try
223                 {
224                     undeployed = ((BundleProvider)deployers[i++]).bundleRemoved(bundle);
225                 }
226                 catch (Exception x)
227                 {
228                     LOG.warn("Error undeploying bundle for jetty context", x);
229                 }
230             }
231         }
232     }
233 
234     public void setAndOpenWebBundleTracker(BundleTracker bundleTracker) {
235         if(_bundleTracker == null) {
236             _bundleTracker = bundleTracker;
237             LOG.debug("Bundle tracker is set");
238             openBundleTracker();
239         }
240     }
241 
242     private void openBundleTracker() {
243         if(_bundleTracker != null && _serviceTracker.getServices() != null &&
244                 _serviceTracker.getServices().length > 0) {
245             _bundleTracker.open();
246             LOG.debug("Bundle tracker has been opened");
247         }
248     }
249 
250 }