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 }