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 }