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 }