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