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