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 }