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 }