View Javadoc

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