001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 * 019 */ 020 021package org.nuxeo.osgi; 022 023import java.io.IOException; 024import java.io.InputStream; 025import java.net.URL; 026import java.util.ArrayList; 027import java.util.Dictionary; 028import java.util.Enumeration; 029import java.util.Map; 030import java.util.jar.Manifest; 031 032import org.nuxeo.common.utils.ExceptionUtils; 033import org.nuxeo.osgi.util.CompoundEnumeration; 034import org.nuxeo.runtime.api.Framework; 035import org.osgi.framework.Bundle; 036import org.osgi.framework.BundleActivator; 037import org.osgi.framework.BundleContext; 038import org.osgi.framework.BundleEvent; 039import org.osgi.framework.BundleException; 040import org.osgi.framework.Constants; 041import org.osgi.framework.ServiceReference; 042import org.osgi.framework.Version; 043import org.osgi.service.packageadmin.PackageAdmin; 044 045/** 046 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a> 047 */ 048public class BundleImpl implements Bundle { 049 050 protected final long id; 051 052 protected final String symbolicName; 053 054 protected final Dictionary<String, String> headers; 055 056 protected final BundleContext context; 057 058 protected final OSGiAdapter osgi; 059 060 protected final BundleFile file; 061 062 protected final String location; 063 064 protected final ClassLoader loader; 065 066 protected int state; 067 068 protected long lastModified; 069 070 protected BundleActivator activator; 071 072 protected double startupTime; 073 074 protected boolean allowHostOverride; 075 076 public BundleImpl(OSGiAdapter osgi, BundleFile file, ClassLoader loader) throws BundleException { 077 this(osgi, file, loader, false); 078 } 079 080 public BundleImpl(OSGiAdapter osgi, BundleFile file, ClassLoader loader, boolean isSystemBundle) 081 throws BundleException { 082 this.osgi = osgi; 083 this.loader = loader; 084 this.file = file; 085 this.location = file.getLocation(); 086 Manifest mf = file.getManifest(); 087 if (mf == null) { 088 headers = null; 089 symbolicName = null; 090 id = -1; 091 context = null; 092 return; 093 } 094 try { 095 headers = BundleManifestReader.getHeaders(mf); 096 } catch (BundleException e) { 097 throw new BundleException("Invalid OSGi Manifest in file " + file + " : " + e.getMessage(), e); 098 } 099 symbolicName = headers.get(Constants.BUNDLE_SYMBOLICNAME); 100 allowHostOverride = Boolean.parseBoolean(headers.get(BundleManifestReader.ALLOW_HOST_OVERRIDE)); 101 id = isSystemBundle ? 0 : osgi.getBundleId(symbolicName); 102 context = createContext(); 103 state = UNINSTALLED; 104 } 105 106 public BundleFile getBundleFile() { 107 return file; 108 } 109 110 protected final BundleContext createContext() { 111 return new OSGiBundleContext(this); 112 } 113 114 @Override 115 public BundleContext getBundleContext() { 116 // ensure BundleContext is not visible in RESOLVED state - to ensure 117 // OSGi compat. - in our component activate method. 118 // TODO NXP-6035: disable for now the check until a better compatibility 119 // mode is implemented. 120 // if (state == RESOLVED) { 121 // throw new IllegalStateException( 122 // "You cannot use a BundleContext when in RESOLVED state. Do not use this in your component activate method!"); 123 // } 124 return context; 125 } 126 127 @Override 128 public void start(int options) throws BundleException { 129 // TODO Auto-generated method stub 130 } 131 132 @Override 133 public void stop(int options) throws BundleException { 134 // TODO 135 } 136 137 @Override 138 public String getLocation() { 139 return location; 140 } 141 142 @Override 143 public URL getResource(String name) { 144 return loader.getResource(name); 145 } 146 147 @Override 148 public Enumeration<URL> getResources(String name) throws IOException { 149 return loader.getResources(name); 150 } 151 152 @Override 153 public Class<?> loadClass(String name) throws ClassNotFoundException { 154 try { 155 return loader.loadClass(name); 156 } catch (NoClassDefFoundError e) { 157 throw e; 158 } 159 } 160 161 @Override 162 public URL getEntry(String name) { 163 return file.getEntry(name); 164 } 165 166 public static PackageAdmin getPackageAdmin() { 167 BundleContext sysctx = Framework.getRuntime().getContext().getBundle().getBundleContext(); 168 ServiceReference ref = sysctx.getServiceReference(PackageAdmin.class.getName()); 169 return (PackageAdmin) sysctx.getService(ref); 170 } 171 172 protected static class CompoundEnumerationBuilder { 173 174 protected final ArrayList<Enumeration<URL>> collected = new ArrayList<>(); 175 176 public CompoundEnumerationBuilder add(Enumeration<URL> e) { 177 collected.add(e); 178 return this; 179 } 180 181 public Enumeration<URL> build() { 182 return new CompoundEnumeration<>(collected.toArray(new Enumeration[collected.size()])); 183 } 184 185 } 186 187 @Override 188 public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) { 189 Enumeration<URL> hostEntries = file.findEntries(path, filePattern, recurse); 190 Bundle[] fragments = osgi.getRegistry().getFragments(symbolicName); 191 if (fragments.length == 0) { 192 return hostEntries; 193 } 194 195 CompoundEnumerationBuilder builder = new CompoundEnumerationBuilder(); 196 if (!allowHostOverride) { 197 builder.add(hostEntries); 198 } 199 200 for (Bundle fragment : fragments) { 201 Enumeration<URL> fragmentEntries = fragment.findEntries(path, filePattern, recurse); 202 builder.add(fragmentEntries); 203 } 204 205 if (allowHostOverride) { 206 builder.add(hostEntries); 207 } 208 209 return builder.build(); 210 } 211 212 @Override 213 public Enumeration<String> getEntryPaths(String path) { 214 return file.getEntryPaths(path); 215 } 216 217 @Override 218 public long getBundleId() { 219 return id; 220 } 221 222 @Override 223 public Dictionary<String, String> getHeaders() { 224 return headers; 225 } 226 227 @Override 228 public Dictionary<String, String> getHeaders(String locale) { 229 return headers; // TODO 230 } 231 232 @Override 233 public long getLastModified() { 234 return lastModified; 235 } 236 237 @Override 238 public ServiceReference[] getRegisteredServices() { 239 // RegistrationInfo ri = 240 // (RegistrationInfo)di.context.get("RegistrationInfo"); 241 // TODO Auto-generated method stub 242 return null; 243 } 244 245 @Override 246 public ServiceReference[] getServicesInUse() { 247 // TODO Auto-generated method stub 248 return null; 249 } 250 251 @Override 252 public int getState() { 253 return state; 254 } 255 256 @Override 257 public String getSymbolicName() { 258 return symbolicName; 259 } 260 261 @Override 262 public boolean hasPermission(Object permission) { 263 return true; // TODO 264 } 265 266 protected String getActivatorClassName() { 267 return headers == null ? null : headers.get(Constants.BUNDLE_ACTIVATOR); 268 } 269 270 public BundleActivator getActivator() throws BundleException { 271 if (activator == null) { 272 activator = NullActivator.INSTANCE; 273 String className = getActivatorClassName(); 274 if (className == null) { 275 return activator; 276 } 277 try { 278 activator = (BundleActivator) loadClass(className).newInstance(); 279 } catch (ClassNotFoundException e) { 280 throw new BundleException("Activator not found: " + className, e); 281 } catch (InstantiationException e) { 282 throw new BundleException("Activator not instantiable: " + className, e); 283 } catch (IllegalAccessException e) { 284 throw new BundleException("Activator not accessible: " + className, e); 285 } 286 } 287 return activator; 288 } 289 290 @Override 291 public void start() throws BundleException { 292 try { 293 setStarting(); 294 getActivator().start(context); 295 setStarted(); 296 } catch (BundleException e) { 297 throw new BundleException("Failed to start bundle at: " + file + " with activator: " 298 + getActivatorClassName(), e); 299 } catch (Exception e) { // stupid OSGi API throws Exception 300 RuntimeException re = ExceptionUtils.runtimeException(e); 301 throw new BundleException("Failed to start bundle at: " + file + " with activator: " 302 + getActivatorClassName(), re); 303 } 304 } 305 306 @Override 307 public void stop() throws BundleException { 308 try { 309 setStopping(); 310 getActivator().stop(context); 311 setStopped(); 312 } catch (BundleException e) { 313 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); 314 } catch (Exception e) { // stupid OSGi API throws Exception 315 RuntimeException re = ExceptionUtils.runtimeException(e); 316 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); 317 } 318 } 319 320 public void shutdown() throws BundleException { 321 try { 322 state = STOPPING; 323 getActivator().stop(context); 324 lastModified = System.currentTimeMillis(); 325 state = UNINSTALLED; 326 } catch (BundleException e) { 327 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); 328 } catch (Exception e) { // stupid OSGi API throws Exception 329 RuntimeException re = ExceptionUtils.runtimeException(e); 330 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); 331 } 332 } 333 334 @Override 335 public void uninstall() throws BundleException { 336 osgi.uninstall(this); 337 try { 338 file.close(); 339 } catch (IOException e) { 340 throw new BundleException("Cannot close underlying file resources " + symbolicName, e); 341 } 342 } 343 344 @Override 345 public void update() throws BundleException { 346 lastModified = System.currentTimeMillis(); 347 throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); 348 } 349 350 @Override 351 public void update(InputStream in) throws BundleException { 352 lastModified = System.currentTimeMillis(); 353 throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); 354 } 355 356 void setInstalled() { 357 if (state == INSTALLED) { 358 return; 359 } 360 lastModified = System.currentTimeMillis(); 361 state = INSTALLED; 362 BundleEvent event = new BundleEvent(BundleEvent.INSTALLED, this); 363 osgi.fireBundleEvent(event); 364 } 365 366 void setUninstalled() { 367 if (state == UNINSTALLED) { 368 return; 369 } 370 lastModified = System.currentTimeMillis(); 371 state = UNINSTALLED; 372 BundleEvent event = new BundleEvent(BundleEvent.UNINSTALLED, this); 373 osgi.fireBundleEvent(event); 374 } 375 376 void setResolved() { 377 if (state == RESOLVED) { 378 return; 379 } 380 state = RESOLVED; 381 BundleEvent event = new BundleEvent(BundleEvent.RESOLVED, this); 382 osgi.fireBundleEvent(event); 383 } 384 385 void setUnResolved() { 386 state = INSTALLED; 387 BundleEvent event = new BundleEvent(BundleEvent.UNRESOLVED, this); 388 osgi.fireBundleEvent(event); 389 } 390 391 void setStarting() { 392 if (state != RESOLVED) { 393 return; 394 } 395 state = STARTING; 396 BundleEvent event = new BundleEvent(BundleEvent.STARTING, this); 397 osgi.fireBundleEvent(event); 398 } 399 400 void setStarted() { 401 if (state != STARTING) { 402 return; 403 } 404 state = ACTIVE; 405 BundleEvent event = new BundleEvent(BundleEvent.STARTED, this); 406 osgi.fireBundleEvent(event); 407 } 408 409 void setStopping() { 410 if (state != ACTIVE) { 411 return; 412 } 413 state = STOPPING; 414 BundleEvent event = new BundleEvent(BundleEvent.STOPPING, this); 415 osgi.fireBundleEvent(event); 416 } 417 418 void setStopped() { 419 if (state != STOPPING) { 420 return; 421 } 422 state = RESOLVED; 423 BundleEvent event = new BundleEvent(BundleEvent.STOPPED, this); 424 osgi.fireBundleEvent(event); 425 } 426 427 public double getStartupTime() { 428 return startupTime; 429 } 430 431 @Override 432 public int hashCode() { 433 return symbolicName.hashCode(); 434 } 435 436 @Override 437 public boolean equals(Object obj) { 438 if (obj instanceof Bundle) { 439 return symbolicName.equals(((Bundle) obj).getSymbolicName()); 440 } 441 return false; 442 } 443 444 @Override 445 public String toString() { 446 return symbolicName; 447 } 448 449 @Override 450 public Map getSignerCertificates(int signersType) { 451 throw new UnsupportedOperationException("not yet implemented"); 452 } 453 454 @Override 455 public Version getVersion() { 456 return Version.parseVersion(headers.get(Constants.BUNDLE_VERSION)); 457 } 458 459}