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 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).getDeclaredConstructor().newInstance(); 279 } catch (ClassNotFoundException e) { 280 throw new BundleException("Activator not found: " + className, e); 281 } catch (ReflectiveOperationException e) { 282 throw new BundleException("Activator not instantiable: " + className, e); 283 } 284 } 285 return activator; 286 } 287 288 @Override 289 public void start() throws BundleException { 290 try { 291 setStarting(); 292 getActivator().start(context); 293 setStarted(); 294 } catch (BundleException e) { 295 throw new BundleException("Failed to start bundle at: " + file + " with activator: " 296 + getActivatorClassName(), e); 297 } catch (Exception e) { // stupid OSGi API throws Exception 298 RuntimeException re = ExceptionUtils.runtimeException(e); 299 throw new BundleException("Failed to start bundle at: " + file + " with activator: " 300 + getActivatorClassName(), re); 301 } 302 } 303 304 @Override 305 public void stop() throws BundleException { 306 try { 307 setStopping(); 308 getActivator().stop(context); 309 setStopped(); 310 } catch (BundleException e) { 311 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); 312 } catch (Exception e) { // stupid OSGi API throws Exception 313 RuntimeException re = ExceptionUtils.runtimeException(e); 314 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); 315 } 316 } 317 318 public void shutdown() throws BundleException { 319 try { 320 state = STOPPING; 321 getActivator().stop(context); 322 lastModified = System.currentTimeMillis(); 323 state = UNINSTALLED; 324 } catch (BundleException e) { 325 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), e); 326 } catch (Exception e) { // stupid OSGi API throws Exception 327 RuntimeException re = ExceptionUtils.runtimeException(e); 328 throw new BundleException("Failed to stop activator: " + getActivatorClassName(), re); 329 } 330 } 331 332 @Override 333 public void uninstall() throws BundleException { 334 osgi.uninstall(this); 335 try { 336 file.close(); 337 } catch (IOException e) { 338 throw new BundleException("Cannot close underlying file resources " + symbolicName, e); 339 } 340 } 341 342 @Override 343 public void update() throws BundleException { 344 lastModified = System.currentTimeMillis(); 345 throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); 346 } 347 348 @Override 349 public void update(InputStream in) throws BundleException { 350 lastModified = System.currentTimeMillis(); 351 throw new UnsupportedOperationException("Bundle.update() operations was not yet implemented"); 352 } 353 354 void setInstalled() { 355 if (state == INSTALLED) { 356 return; 357 } 358 lastModified = System.currentTimeMillis(); 359 state = INSTALLED; 360 BundleEvent event = new BundleEvent(BundleEvent.INSTALLED, this); 361 osgi.fireBundleEvent(event); 362 } 363 364 void setUninstalled() { 365 if (state == UNINSTALLED) { 366 return; 367 } 368 lastModified = System.currentTimeMillis(); 369 state = UNINSTALLED; 370 BundleEvent event = new BundleEvent(BundleEvent.UNINSTALLED, this); 371 osgi.fireBundleEvent(event); 372 } 373 374 void setResolved() { 375 if (state == RESOLVED) { 376 return; 377 } 378 state = RESOLVED; 379 BundleEvent event = new BundleEvent(BundleEvent.RESOLVED, this); 380 osgi.fireBundleEvent(event); 381 } 382 383 void setUnResolved() { 384 state = INSTALLED; 385 BundleEvent event = new BundleEvent(BundleEvent.UNRESOLVED, this); 386 osgi.fireBundleEvent(event); 387 } 388 389 void setStarting() { 390 if (state != RESOLVED) { 391 return; 392 } 393 state = STARTING; 394 BundleEvent event = new BundleEvent(BundleEvent.STARTING, this); 395 osgi.fireBundleEvent(event); 396 } 397 398 void setStarted() { 399 if (state != STARTING) { 400 return; 401 } 402 state = ACTIVE; 403 BundleEvent event = new BundleEvent(BundleEvent.STARTED, this); 404 osgi.fireBundleEvent(event); 405 } 406 407 void setStopping() { 408 if (state != ACTIVE) { 409 return; 410 } 411 state = STOPPING; 412 BundleEvent event = new BundleEvent(BundleEvent.STOPPING, this); 413 osgi.fireBundleEvent(event); 414 } 415 416 void setStopped() { 417 if (state != STOPPING) { 418 return; 419 } 420 state = RESOLVED; 421 BundleEvent event = new BundleEvent(BundleEvent.STOPPED, this); 422 osgi.fireBundleEvent(event); 423 } 424 425 public double getStartupTime() { 426 return startupTime; 427 } 428 429 @Override 430 public int hashCode() { 431 return symbolicName.hashCode(); 432 } 433 434 @Override 435 public boolean equals(Object obj) { 436 if (obj instanceof Bundle) { 437 return symbolicName.equals(((Bundle) obj).getSymbolicName()); 438 } 439 return false; 440 } 441 442 @Override 443 public String toString() { 444 return symbolicName; 445 } 446 447 @SuppressWarnings("rawtypes") 448 @Override 449 public Map getSignerCertificates(int signersType) { 450 throw new UnsupportedOperationException("not yet implemented"); 451 } 452 453 @Override 454 public Version getVersion() { 455 return Version.parseVersion(headers.get(Constants.BUNDLE_VERSION)); 456 } 457 458}