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  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  
29  import org.eclipse.jetty.osgi.boot.BundleProvider;
30  import org.eclipse.jetty.osgi.boot.OSGiServerConstants;
31  import org.eclipse.jetty.osgi.boot.utils.TldBundleDiscoverer;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.osgi.framework.Bundle;
35  import org.osgi.framework.BundleEvent;
36  import org.osgi.framework.FrameworkUtil;
37  import org.osgi.framework.ServiceReference;
38  import org.osgi.util.tracker.BundleTracker;
39  import org.osgi.util.tracker.BundleTrackerCustomizer;
40  import org.osgi.util.tracker.ServiceTracker;
41  
42  /**
43   * BundleWatcher
44   * 
45   * 
46   * Tracks the installation and removal of Bundles in the OSGi environment. Any bundles
47   * that are added are passed to the set of Jetty DeploymentManager providers to see if
48   * the bundle should be deployed as a webapp or ContextHandler into Jetty.
49   * 
50   * @author hmalphettes
51   */
52  public class BundleWatcher implements BundleTrackerCustomizer
53  {
54      private static final Logger LOG = Log.getLogger(BundleWatcher.class);
55      
56      public static final Collection<TldBundleDiscoverer> JSP_REGISTRATION_HELPERS = new ArrayList<TldBundleDiscoverer>();
57  
58  
59      public static final String FILTER = "(objectclass=" + BundleProvider.class.getName() + ")";
60      private ServiceTracker _serviceTracker;
61      private BundleTracker _bundleTracker;
62      private boolean _waitForDefaultServer = true;
63      private boolean _defaultServerReady = false;
64      private Bundle _bundle = null;
65      
66   
67      
68      /* ------------------------------------------------------------ */
69      /**
70       * @throws Exception
71       */
72      public BundleWatcher() throws Exception
73      {
74          _bundle = FrameworkUtil.getBundle(this.getClass());
75          //Track all BundleProviders (Jetty DeploymentManager Providers that can deploy bundles)
76          _serviceTracker = new ServiceTracker(_bundle.getBundleContext(), FrameworkUtil.createFilter(FILTER),null);
77          _serviceTracker.open();
78      }
79      
80      
81      /* ------------------------------------------------------------ */
82      public boolean isWaitForDefaultServer()
83      {
84          return _waitForDefaultServer;
85      }
86  
87  
88      /* ------------------------------------------------------------ */
89      public void setWaitForDefaultServer(boolean waitForDefaultServer)
90      {
91          _waitForDefaultServer = waitForDefaultServer;
92      }
93  
94      
95      /* ------------------------------------------------------------ */
96      public void setBundleTracker (BundleTracker bundleTracker)
97      {
98          _bundleTracker = bundleTracker;
99      }
100 
101     
102     /* ------------------------------------------------------------ */
103     public void open () throws Exception
104     {
105         if (_waitForDefaultServer && !_defaultServerReady)
106         {
107             String filter = "(&(objectclass=" + BundleProvider.class.getName() + ")"+
108                     "("+OSGiServerConstants.MANAGED_JETTY_SERVER_NAME+"="+OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME+"))";
109             
110             ServiceTracker defaultServerTracker = new ServiceTracker(_bundle.getBundleContext(), 
111                                                                      FrameworkUtil.createFilter(filter),null)
112             {
113                 public Object addingService(ServiceReference reference)
114                 {
115                     try
116                     {
117                         Object object = super.addingService(reference);
118                         LOG.debug("Default Jetty Server registered {}", reference);
119                         _defaultServerReady = true;
120                         openBundleTracker();
121                         return object;
122                     }
123                     catch (Exception e)
124                     {
125                         throw new IllegalStateException(e);
126                     }
127                 }
128             };
129             defaultServerTracker.open();
130         }
131         else
132             openBundleTracker();
133     }
134 
135     /* ------------------------------------------------------------ */
136     /**
137      * @param managedServerName
138      * @return
139      */
140     public Map<ServiceReference, BundleProvider> getDeployers(String managedServerName)
141     {
142         if (managedServerName == null)
143             managedServerName = OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME;
144         
145         Map<ServiceReference, BundleProvider> candidates = new HashMap<ServiceReference, BundleProvider>();
146         
147         ServiceReference[] references = _serviceTracker.getServiceReferences();
148         if (references != null)
149         {
150             for (ServiceReference ref:references)
151             {
152                 String name = (String)ref.getProperty(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);                
153                 if (managedServerName.equalsIgnoreCase(name))
154                 {
155                     BundleProvider candidate = (BundleProvider)_serviceTracker.getService(ref);
156                     if (candidate != null)
157                         candidates.put(ref, candidate);
158                 }
159             }
160         }
161        return candidates;
162     }
163 
164     /* ------------------------------------------------------------ */
165     /**
166      * A bundle is being added to the <code>BundleTracker</code>.
167      * 
168      * <p>
169      * This method is called before a bundle which matched the search parameters
170      * of the <code>BundleTracker</code> is added to the
171      * <code>BundleTracker</code>. This method should return the object to be
172      * tracked for the specified <code>Bundle</code>. The returned object is
173      * stored in the <code>BundleTracker</code> and is available from the
174      * {@link BundleTracker#getObject(Bundle) getObject} method.
175      * 
176      * @param bundle The <code>Bundle</code> being added to the
177      *            <code>BundleTracker</code>.
178      * @param event The bundle event which caused this customizer method to be
179      *            called or <code>null</code> if there is no bundle event
180      *            associated with the call to this method.
181      * @return The object to be tracked for the specified <code>Bundle</code>
182      *         object or <code>null</code> if the specified <code>Bundle</code>
183      *         object should not be tracked.
184      */
185     public Object addingBundle(Bundle bundle, BundleEvent event)
186     {
187         if (bundle.getState() == Bundle.ACTIVE)
188         {
189             register(bundle);          
190         }
191         else if (bundle.getState() == Bundle.STOPPING)
192         {
193             unregister(bundle);
194         }
195         else
196         {
197             // we should not be called in that state as
198             // we are registered only for ACTIVE and STOPPING
199         }
200         return null;
201     }
202 
203     
204     /* ------------------------------------------------------------ */
205     /**
206      * A bundle tracked by the <code>BundleTracker</code> has been modified.
207      * 
208      * <p>
209      * This method is called when a bundle being tracked by the
210      * <code>BundleTracker</code> has had its state modified.
211      * 
212      * @param bundle The <code>Bundle</code> whose state has been modified.
213      * @param event The bundle event which caused this customizer method to be
214      *            called or <code>null</code> if there is no bundle event
215      *            associated with the call to this method.
216      * @param object The tracked object for the specified bundle.
217      */
218     public void modifiedBundle(Bundle bundle, BundleEvent event, Object object)
219     {
220         if (bundle.getState() == Bundle.STOPPING || bundle.getState() == Bundle.ACTIVE)
221         {
222             unregister(bundle);
223         }
224         if (bundle.getState() == Bundle.ACTIVE)
225         {
226             register(bundle);
227         }
228     }
229 
230     
231     /* ------------------------------------------------------------ */
232     /**
233      * A bundle tracked by the <code>BundleTracker</code> has been removed.
234      * 
235      * <p>
236      * This method is called after a bundle is no longer being tracked by the
237      * <code>BundleTracker</code>.
238      * 
239      * @param bundle The <code>Bundle</code> that has been removed.
240      * @param event The bundle event which caused this customizer method to be
241      *            called or <code>null</code> if there is no bundle event
242      *            associated with the call to this method.
243      * @param object The tracked object for the specified bundle.
244      */
245     public void removedBundle(Bundle bundle, BundleEvent event, Object object)
246     {
247         unregister(bundle);
248     }
249 
250     
251     protected void openBundleTracker()
252     {
253         _bundleTracker.open();
254     }
255     
256     /* ------------------------------------------------------------ */
257     /**
258      * @param bundle
259      * @return true if this bundle can be deployed into Jetty
260      */
261     private boolean register(Bundle bundle)
262     {
263         if (bundle == null)
264             return false;
265 
266         //It might be a bundle that is deployable by Jetty.
267         //Use any named Server instance provided, defaulting to the default Server instance if none supplied
268         boolean deployed = false;
269         String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);
270         Map<ServiceReference, BundleProvider> candidates = getDeployers(serverName);
271         if (candidates != null)
272         {
273             Iterator<Entry<ServiceReference, BundleProvider>> itor = candidates.entrySet().iterator();
274             while (!deployed && itor.hasNext())
275             {
276                 Entry<ServiceReference, BundleProvider> e = itor.next();
277                 try
278                 {           
279                     deployed = e.getValue().bundleAdded(bundle);
280                 }
281                 catch (Exception x)
282                 {
283                     LOG.warn("Error deploying bundle for jetty context", x);
284                 }
285             }
286         }
287 
288         return deployed;
289     }
290 
291     /* ------------------------------------------------------------ */
292     /**
293      * @param bundle
294      */
295     private void unregister(Bundle bundle)
296     { 
297         boolean undeployed = false;
298         String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME);    
299         Map<ServiceReference, BundleProvider> candidates = getDeployers(serverName);
300         if (candidates != null)
301         {
302             Iterator<Entry<ServiceReference, BundleProvider>> itor = candidates.entrySet().iterator();
303             while (!undeployed && itor.hasNext())
304             {
305                 Entry<ServiceReference, BundleProvider> e = itor.next();
306                 try
307                 {
308                     undeployed = e.getValue().bundleRemoved(bundle);
309                 }
310                 catch (Exception x)
311                 {
312                     LOG.warn("Error undeploying Bundle representing jetty deployable ", x);
313                 }
314             }
315         }
316     }
317 }