1 /******************************************************************************* 2 * Copyright (c) 2012, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Christoph Laeubrich - Bug 527175 - Storage#getSystemContent() should first make the file absolute 14 *******************************************************************************/ 15 package org.eclipse.osgi.storage; 16 17 import java.io.BufferedInputStream; 18 import java.io.BufferedOutputStream; 19 import java.io.ByteArrayInputStream; 20 import java.io.ByteArrayOutputStream; 21 import java.io.DataInputStream; 22 import java.io.DataOutputStream; 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.lang.reflect.Method; 29 import java.net.MalformedURLException; 30 import java.net.URL; 31 import java.net.URLConnection; 32 import java.nio.charset.StandardCharsets; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.security.PrivilegedActionException; 36 import java.security.PrivilegedExceptionAction; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.Collections; 41 import java.util.Dictionary; 42 import java.util.Enumeration; 43 import java.util.HashMap; 44 import java.util.Hashtable; 45 import java.util.Iterator; 46 import java.util.LinkedHashSet; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.NoSuchElementException; 50 import java.util.Properties; 51 import java.util.Set; 52 import java.util.StringTokenizer; 53 import java.util.concurrent.atomic.AtomicBoolean; 54 import org.eclipse.core.runtime.adaptor.EclipseStarter; 55 import org.eclipse.osgi.container.Module; 56 import org.eclipse.osgi.container.ModuleCapability; 57 import org.eclipse.osgi.container.ModuleContainer; 58 import org.eclipse.osgi.container.ModuleContainerAdaptor; 59 import org.eclipse.osgi.container.ModuleDatabase; 60 import org.eclipse.osgi.container.ModuleRevision; 61 import org.eclipse.osgi.container.ModuleRevisionBuilder; 62 import org.eclipse.osgi.container.ModuleWire; 63 import org.eclipse.osgi.container.ModuleWiring; 64 import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory; 65 import org.eclipse.osgi.container.namespaces.EclipsePlatformNamespace; 66 import org.eclipse.osgi.framework.log.FrameworkLogEntry; 67 import org.eclipse.osgi.framework.util.FilePath; 68 import org.eclipse.osgi.framework.util.ObjectPool; 69 import org.eclipse.osgi.framework.util.SecureAction; 70 import org.eclipse.osgi.internal.debug.Debug; 71 import org.eclipse.osgi.internal.framework.EquinoxConfiguration; 72 import org.eclipse.osgi.internal.framework.EquinoxContainer; 73 import org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor; 74 import org.eclipse.osgi.internal.framework.FilterImpl; 75 import org.eclipse.osgi.internal.hookregistry.BundleFileWrapperFactoryHook; 76 import org.eclipse.osgi.internal.hookregistry.StorageHookFactory; 77 import org.eclipse.osgi.internal.hookregistry.StorageHookFactory.StorageHook; 78 import org.eclipse.osgi.internal.location.EquinoxLocations; 79 import org.eclipse.osgi.internal.location.LocationHelper; 80 import org.eclipse.osgi.internal.log.EquinoxLogServices; 81 import org.eclipse.osgi.internal.messages.Msg; 82 import org.eclipse.osgi.internal.permadmin.SecurityAdmin; 83 import org.eclipse.osgi.internal.url.URLStreamHandlerFactoryImpl; 84 import org.eclipse.osgi.service.datalocation.Location; 85 import org.eclipse.osgi.storage.BundleInfo.Generation; 86 import org.eclipse.osgi.storage.bundlefile.BundleEntry; 87 import org.eclipse.osgi.storage.bundlefile.BundleFile; 88 import org.eclipse.osgi.storage.bundlefile.BundleFileWrapper; 89 import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain; 90 import org.eclipse.osgi.storage.bundlefile.DirBundleFile; 91 import org.eclipse.osgi.storage.bundlefile.MRUBundleFileList; 92 import org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile; 93 import org.eclipse.osgi.storage.bundlefile.ZipBundleFile; 94 import org.eclipse.osgi.storage.url.reference.Handler; 95 import org.eclipse.osgi.storage.url.reference.ReferenceInputStream; 96 import org.eclipse.osgi.storagemanager.ManagedOutputStream; 97 import org.eclipse.osgi.storagemanager.StorageManager; 98 import org.eclipse.osgi.util.ManifestElement; 99 import org.eclipse.osgi.util.NLS; 100 import org.osgi.framework.Bundle; 101 import org.osgi.framework.BundleContext; 102 import org.osgi.framework.BundleException; 103 import org.osgi.framework.Constants; 104 import org.osgi.framework.Filter; 105 import org.osgi.framework.InvalidSyntaxException; 106 import org.osgi.framework.Version; 107 import org.osgi.framework.namespace.HostNamespace; 108 import org.osgi.framework.namespace.NativeNamespace; 109 import org.osgi.framework.namespace.PackageNamespace; 110 import org.osgi.framework.wiring.BundleCapability; 111 import org.osgi.framework.wiring.BundleRevision; 112 import org.osgi.framework.wiring.BundleWiring; 113 import org.osgi.resource.Namespace; 114 import org.osgi.resource.Requirement; 115 116 public class Storage { 117 public static class StorageException extends RuntimeException { 118 private static final long serialVersionUID = 1L; 119 StorageException()120 public StorageException() { 121 super(); 122 } 123 StorageException(String message, Throwable cause)124 public StorageException(String message, Throwable cause) { 125 super(message, cause); 126 } 127 StorageException(String message)128 public StorageException(String message) { 129 super(message); 130 } 131 StorageException(Throwable cause)132 public StorageException(Throwable cause) { 133 super(cause); 134 } 135 136 } 137 138 public static final int VERSION = 5; 139 private static final int MR_JAR_VERSION = 4; 140 private static final int CACHED_SYSTEM_CAPS_VERION = 5; 141 private static final int LOWEST_VERSION_SUPPORTED = 3; 142 public static final String BUNDLE_DATA_DIR = "data"; //$NON-NLS-1$ 143 public static final String BUNDLE_FILE_NAME = "bundleFile"; //$NON-NLS-1$ 144 public static final String FRAMEWORK_INFO = "framework.info"; //$NON-NLS-1$ 145 public static final String ECLIPSE_SYSTEMBUNDLE = "Eclipse-SystemBundle"; //$NON-NLS-1$ 146 public static final String DELETE_FLAG = ".delete"; //$NON-NLS-1$ 147 public static final String LIB_TEMP = "libtemp"; //$NON-NLS-1$ 148 149 private static final String JAVASE = "JavaSE"; //$NON-NLS-1$ 150 private static final String PROFILE_EXT = ".profile"; //$NON-NLS-1$ 151 private static final String NUL = new String(new byte[] {0}); 152 private static final String INITIAL_LOCATION = "initial@"; //$NON-NLS-1$ 153 154 static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction()); 155 156 private final EquinoxContainer equinoxContainer; 157 private final String installPath; 158 private final Location osgiLocation; 159 private final File childRoot; 160 private final File parentRoot; 161 private final PermissionData permissionData; 162 private final SecurityAdmin securityAdmin; 163 private final EquinoxContainerAdaptor adaptor; 164 private final ModuleDatabase moduleDatabase; 165 private final ModuleContainer moduleContainer; 166 private final Object saveMonitor = new Object(); 167 private long lastSavedTimestamp = -1; 168 private final MRUBundleFileList mruList; 169 private final FrameworkExtensionInstaller extensionInstaller; 170 private final List<String> cachedHeaderKeys = Arrays.asList(Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_ACTIVATIONPOLICY, "Service-Component"); //$NON-NLS-1$ 171 private final boolean allowRestrictedProvides; 172 private final AtomicBoolean refreshMRBundles = new AtomicBoolean(false); 173 private final Version runtimeVersion; 174 private final String javaSpecVersion; 175 createStorage(EquinoxContainer container)176 public static Storage createStorage(EquinoxContainer container) throws IOException, BundleException { 177 String[] cachedInfo = new String[3]; 178 Storage storage = new Storage(container, cachedInfo); 179 // Do some operations that need to happen on the fully constructed Storage before returning it 180 storage.checkSystemBundle(cachedInfo); 181 storage.refreshStaleBundles(); 182 storage.installExtensions(); 183 // TODO hack to make sure all bundles are in UNINSTALLED state before system bundle init is called 184 storage.getModuleContainer().setInitialModuleStates(); 185 return storage; 186 } 187 Storage(EquinoxContainer container, String[] cachedInfo)188 private Storage(EquinoxContainer container, String[] cachedInfo) throws IOException { 189 // default to Java 7 since that is our min 190 Version javaVersion = Version.valueOf("1.7"); //$NON-NLS-1$ 191 // set the profile and EE based off of the java.specification.version 192 String javaSpecVersionProp = System.getProperty(EquinoxConfiguration.PROP_JVM_SPEC_VERSION); 193 StringTokenizer st = new StringTokenizer(javaSpecVersionProp, " _-"); //$NON-NLS-1$ 194 javaSpecVersionProp = st.nextToken(); 195 try { 196 String[] vComps = javaSpecVersionProp.split("\\."); //$NON-NLS-1$ 197 // only pay attention to the first three components of the version 198 int major = vComps.length > 0 ? Integer.parseInt(vComps[0]) : 0; 199 int minor = vComps.length > 1 ? Integer.parseInt(vComps[1]) : 0; 200 int micro = vComps.length > 2 ? Integer.parseInt(vComps[2]) : 0; 201 javaVersion = new Version(major, minor, micro); 202 } catch (IllegalArgumentException e) { 203 // do nothing 204 } 205 runtimeVersion = javaVersion; 206 javaSpecVersion = javaSpecVersionProp; 207 mruList = new MRUBundleFileList(getBundleFileLimit(container.getConfiguration()), container.getConfiguration().getDebug()); 208 equinoxContainer = container; 209 extensionInstaller = new FrameworkExtensionInstaller(container.getConfiguration()); 210 allowRestrictedProvides = Boolean.parseBoolean(container.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_ALLOW_RESTRICTED_PROVIDES)); 211 212 // we need to set the install path as soon as possible so we can determine 213 // the absolute location of install relative URLs 214 Location installLoc = container.getLocations().getInstallLocation(); 215 URL installURL = installLoc.getURL(); 216 // assume install URL is file: based 217 installPath = installURL.getPath(); 218 219 Location configLocation = container.getLocations().getConfigurationLocation(); 220 Location parentConfigLocation = configLocation.getParentLocation(); 221 Location osgiParentLocation = null; 222 if (parentConfigLocation != null) { 223 osgiParentLocation = parentConfigLocation.createLocation(null, parentConfigLocation.getDataArea(EquinoxContainer.NAME), true); 224 } 225 this.osgiLocation = configLocation.createLocation(osgiParentLocation, configLocation.getDataArea(EquinoxContainer.NAME), configLocation.isReadOnly()); 226 this.childRoot = new File(osgiLocation.getURL().getPath()); 227 228 if (Boolean.valueOf(container.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_CLEAN)).booleanValue()) { 229 cleanOSGiStorage(osgiLocation, childRoot); 230 } 231 if (!this.osgiLocation.isReadOnly()) { 232 this.childRoot.mkdirs(); 233 } 234 Location parent = this.osgiLocation.getParentLocation(); 235 parentRoot = parent == null ? null : new File(parent.getURL().getPath()); 236 237 if (container.getConfiguration().getConfiguration(Constants.FRAMEWORK_STORAGE) == null) { 238 // Set the derived value if not already set as part of configuration. 239 // Note this is the parent directory of where the framework stores data (org.eclipse.osgi/) 240 container.getConfiguration().setConfiguration(Constants.FRAMEWORK_STORAGE, childRoot.getParentFile().getAbsolutePath()); 241 } 242 243 InputStream info = getInfoInputStream(); 244 DataInputStream data = info == null ? null : new DataInputStream(new BufferedInputStream(info)); 245 try { 246 Map<Long, Generation> generations; 247 try { 248 generations = loadGenerations(data, cachedInfo); 249 } catch (IllegalArgumentException e) { 250 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, "The persistent format for the framework data has changed. The framework will be reinitialized: " + e.getMessage(), null); //$NON-NLS-1$ 251 generations = new HashMap<>(0); 252 data = null; 253 cleanOSGiStorage(osgiLocation, childRoot); 254 } 255 this.permissionData = loadPermissionData(data); 256 this.securityAdmin = new SecurityAdmin(null, this.permissionData); 257 this.adaptor = new EquinoxContainerAdaptor(equinoxContainer, this, generations); 258 this.moduleDatabase = new ModuleDatabase(this.adaptor); 259 this.moduleContainer = new ModuleContainer(this.adaptor, this.moduleDatabase); 260 if (data != null) { 261 try { 262 moduleDatabase.load(data); 263 lastSavedTimestamp = moduleDatabase.getTimestamp(); 264 } catch (IllegalArgumentException e) { 265 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, "Incompatible version. Starting with empty framework.", e); //$NON-NLS-1$ 266 // Clean up the cache. 267 // No need to clean up the database. Nothing got loaded. 268 cleanOSGiStorage(osgiLocation, childRoot); 269 // should free up the generations map 270 generations.clear(); 271 } 272 } 273 } finally { 274 if (data != null) { 275 try { 276 data.close(); 277 } catch (IOException e) { 278 // just move on 279 } 280 } 281 } 282 } 283 getRuntimeVersion()284 public Version getRuntimeVersion() { 285 return runtimeVersion; 286 } 287 getMRUBundleFileList()288 public MRUBundleFileList getMRUBundleFileList() { 289 return mruList; 290 } 291 getBundleFileLimit(EquinoxConfiguration configuration)292 private int getBundleFileLimit(EquinoxConfiguration configuration) { 293 int propValue = 100; // enable to 100 open files by default 294 try { 295 String prop = configuration.getConfiguration(EquinoxConfiguration.PROP_FILE_LIMIT); 296 if (prop != null) 297 propValue = Integer.parseInt(prop); 298 } catch (NumberFormatException e) { 299 // use default of 100 300 } 301 return propValue; 302 } 303 installExtensions()304 private void installExtensions() { 305 Module systemModule = moduleContainer.getModule(0); 306 ModuleRevision systemRevision = systemModule == null ? null : systemModule.getCurrentRevision(); 307 ModuleWiring systemWiring = systemRevision == null ? null : systemRevision.getWiring(); 308 if (systemWiring == null) { 309 return; 310 } 311 Collection<ModuleRevision> fragments = new ArrayList<>(); 312 for (ModuleWire hostWire : systemWiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE)) { 313 fragments.add(hostWire.getRequirer()); 314 } 315 try { 316 getExtensionInstaller().addExtensionContent(fragments, null); 317 } catch (BundleException e) { 318 getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, e.getMessage(), e); 319 } 320 } 321 loadPermissionData(DataInputStream in)322 private static PermissionData loadPermissionData(DataInputStream in) throws IOException { 323 PermissionData permData = new PermissionData(); 324 if (in != null) { 325 permData.readPermissionData(in); 326 } 327 return permData; 328 } 329 refreshStaleBundles()330 private void refreshStaleBundles() throws BundleException { 331 Collection<Module> needsRefresh = new ArrayList<>(0); 332 333 // First uninstall any modules that had their content changed or deleted 334 for (Module module : moduleContainer.getModules()) { 335 if (module.getId() == Constants.SYSTEM_BUNDLE_ID) 336 continue; 337 ModuleRevision revision = module.getCurrentRevision(); 338 Generation generation = (Generation) revision.getRevisionInfo(); 339 if (needsDiscarding(generation)) { 340 needsRefresh.add(module); 341 moduleContainer.uninstall(module); 342 generation.delete(); 343 } 344 } 345 // Next check if we need to refresh Multi-Release Jar bundles 346 // because the runtime version changed. 347 if (refreshMRBundles.get()) { 348 needsRefresh.addAll(refreshMRJarBundles()); 349 } 350 351 // refresh the modules that got deleted or are Multi-Release bundles 352 if (!needsRefresh.isEmpty()) { 353 moduleContainer.refresh(needsRefresh); 354 } 355 } 356 needsDiscarding(Generation generation)357 private boolean needsDiscarding(Generation generation) { 358 for (StorageHook<?, ?> hook : generation.getStorageHooks()) { 359 try { 360 hook.validate(); 361 } catch (IllegalStateException e) { 362 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, "Error validating installed bundle.", e); //$NON-NLS-1$ 363 return true; 364 } 365 } 366 File content = generation.getContent(); 367 if (getConfiguration().inCheckConfigurationMode()) { 368 if (generation.isDirectory()) { 369 content = new File(content, "META-INF/MANIFEST.MF"); //$NON-NLS-1$ 370 } 371 return generation.getLastModified() != secureAction.lastModified(content); 372 } 373 if (!content.exists()) { 374 // the content got deleted since last time! 375 return true; 376 } 377 return false; 378 } 379 checkSystemBundle(String[] cachedInfo)380 private void checkSystemBundle(String[] cachedInfo) { 381 Module systemModule = moduleContainer.getModule(0); 382 Generation newGeneration = null; 383 try { 384 if (systemModule == null) { 385 BundleInfo info = new BundleInfo(this, 0, Constants.SYSTEM_BUNDLE_LOCATION, 0); 386 newGeneration = info.createGeneration(); 387 388 File contentFile = getSystemContent(); 389 newGeneration.setContent(contentFile, false); 390 391 // First we must make sure the VM profile has been loaded 392 loadVMProfile(newGeneration); 393 // dealing with system bundle find the extra capabilities and exports 394 String extraCapabilities = getSystemExtraCapabilities(); 395 String extraExports = getSystemExtraPackages(); 396 397 ModuleRevisionBuilder builder = getBuilder(newGeneration, extraCapabilities, extraExports); 398 systemModule = moduleContainer.install(null, Constants.SYSTEM_BUNDLE_LOCATION, builder, newGeneration); 399 moduleContainer.resolve(Collections.singletonList(systemModule), false); 400 } else { 401 ModuleRevision currentRevision = systemModule.getCurrentRevision(); 402 Generation currentGeneration = currentRevision == null ? null : (Generation) currentRevision.getRevisionInfo(); 403 if (currentGeneration == null) { 404 throw new IllegalStateException("No current revision for system bundle."); //$NON-NLS-1$ 405 } 406 try { 407 // First we must make sure the VM profile has been loaded 408 loadVMProfile(currentGeneration); 409 // dealing with system bundle find the extra capabilities and exports 410 String extraCapabilities = getSystemExtraCapabilities(); 411 String extraExports = getSystemExtraPackages(); 412 File contentFile = currentGeneration.getContent(); 413 if (systemNeedsUpdate(contentFile, currentRevision, currentGeneration, extraCapabilities, extraExports, cachedInfo)) { 414 newGeneration = currentGeneration.getBundleInfo().createGeneration(); 415 newGeneration.setContent(contentFile, false); 416 ModuleRevisionBuilder newBuilder = getBuilder(newGeneration, extraCapabilities, extraExports); 417 moduleContainer.update(systemModule, newBuilder, newGeneration); 418 moduleContainer.refresh(Collections.singleton(systemModule)); 419 } else { 420 if (currentRevision.getWiring() == null) { 421 // must resolve before continuing to ensure extensions get attached 422 moduleContainer.resolve(Collections.singleton(systemModule), true); 423 } 424 } 425 } catch (BundleException e) { 426 throw new IllegalStateException("Could not create a builder for the system bundle.", e); //$NON-NLS-1$ 427 } 428 } 429 ModuleRevision currentRevision = systemModule.getCurrentRevision(); 430 List<ModuleCapability> nativeEnvironments = currentRevision.getModuleCapabilities(NativeNamespace.NATIVE_NAMESPACE); 431 Map<String, Object> configMap = equinoxContainer.getConfiguration().getInitialConfig(); 432 for (ModuleCapability nativeEnvironment : nativeEnvironments) { 433 nativeEnvironment.setTransientAttrs(configMap); 434 } 435 Version frameworkVersion = null; 436 if (newGeneration != null) { 437 frameworkVersion = findFrameworkVersion(); 438 } else { 439 String sVersion = cachedInfo[0]; 440 frameworkVersion = sVersion == null ? findFrameworkVersion() : Version.parseVersion(sVersion); 441 } 442 if (frameworkVersion != null) { 443 this.equinoxContainer.getConfiguration().setConfiguration(Constants.FRAMEWORK_VERSION, frameworkVersion.toString()); 444 } 445 } catch (Exception e) { 446 if (e instanceof RuntimeException) { 447 throw (RuntimeException) e; 448 } 449 throw new RuntimeException("Error occurred while checking the system module.", e); //$NON-NLS-1$ 450 } finally { 451 if (newGeneration != null) { 452 newGeneration.getBundleInfo().unlockGeneration(newGeneration); 453 } 454 } 455 } 456 findFrameworkVersion()457 private Version findFrameworkVersion() { 458 Requirement osgiPackageReq = ModuleContainer.createRequirement(PackageNamespace.PACKAGE_NAMESPACE, Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, "(" + PackageNamespace.PACKAGE_NAMESPACE + "=org.osgi.framework)"), Collections.<String, String> emptyMap()); //$NON-NLS-1$ //$NON-NLS-2$ 459 Collection<BundleCapability> osgiPackages = moduleContainer.getFrameworkWiring().findProviders(osgiPackageReq); 460 for (BundleCapability packageCapability : osgiPackages) { 461 if (packageCapability.getRevision().getBundle().getBundleId() == 0) { 462 Version v = (Version) packageCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); 463 if (v != null) { 464 return v; 465 } 466 } 467 } 468 return null; 469 } 470 refreshMRJarBundles()471 private Collection<Module> refreshMRJarBundles() throws BundleException { 472 Collection<Module> mrJarBundles = new ArrayList<>(); 473 for (Module m : moduleContainer.getModules()) { 474 Generation generation = (Generation) m.getCurrentRevision().getRevisionInfo(); 475 // Note that we check the raw headers here incase we are working off an old version of the persistent storage 476 if (Boolean.parseBoolean(generation.getRawHeaders().get(BundleInfo.MULTI_RELEASE_HEADER))) { 477 refresh(m); 478 mrJarBundles.add(m); 479 } 480 } 481 return mrJarBundles; 482 } 483 close()484 public void close() { 485 try { 486 save(); 487 } catch (IOException e) { 488 getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error saving on shutdown", e); //$NON-NLS-1$ 489 } 490 491 // close all the generations 492 List<Module> modules = moduleContainer.getModules(); 493 for (Module module : modules) { 494 for (ModuleRevision revision : module.getRevisions().getModuleRevisions()) { 495 Generation generation = (Generation) revision.getRevisionInfo(); 496 if (generation != null) { 497 generation.close(); 498 } 499 } 500 } 501 for (ModuleRevision removalPending : moduleContainer.getRemovalPending()) { 502 Generation generation = (Generation) removalPending.getRevisionInfo(); 503 if (generation != null) { 504 generation.close(); 505 } 506 } 507 mruList.shutdown(); 508 adaptor.shutdownExecutors(); 509 } 510 systemNeedsUpdate(File systemContent, ModuleRevision currentRevision, Generation existing, String extraCapabilities, String extraExports, String[] cachedInfo)511 private boolean systemNeedsUpdate(File systemContent, ModuleRevision currentRevision, Generation existing, String extraCapabilities, String extraExports, String[] cachedInfo) throws BundleException { 512 if (!extraCapabilities.equals(cachedInfo[1])) { 513 return true; 514 } 515 if (!extraExports.equals(cachedInfo[2])) { 516 return true; 517 } 518 if (systemContent == null) { 519 // only do a version check in this case 520 ModuleRevisionBuilder newBuilder = getBuilder(existing, extraCapabilities, extraExports); 521 return !currentRevision.getVersion().equals(newBuilder.getVersion()); 522 } 523 if (existing.isDirectory()) { 524 systemContent = new File(systemContent, "META-INF/MANIFEST.MF"); //$NON-NLS-1$ 525 } 526 return existing.getLastModified() != secureAction.lastModified(systemContent); 527 528 } 529 cleanOSGiStorage(Location location, File root)530 private void cleanOSGiStorage(Location location, File root) { 531 if (location.isReadOnly() || !StorageUtil.rm(root, getConfiguration().getDebug().DEBUG_STORAGE)) { 532 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "The -clean (osgi.clean) option was not successful. Unable to clean the storage area: " + root.getAbsolutePath(), null); //$NON-NLS-1$ 533 } 534 if (!location.isReadOnly()) { 535 // make sure to recreate to root folder 536 root.mkdirs(); 537 } 538 } 539 getModuleDatabase()540 public ModuleDatabase getModuleDatabase() { 541 return moduleDatabase; 542 } 543 getAdaptor()544 public ModuleContainerAdaptor getAdaptor() { 545 return adaptor; 546 } 547 getModuleContainer()548 public ModuleContainer getModuleContainer() { 549 return moduleContainer; 550 } 551 getConfiguration()552 public EquinoxConfiguration getConfiguration() { 553 return equinoxContainer.getConfiguration(); 554 } 555 getLogServices()556 public EquinoxLogServices getLogServices() { 557 return equinoxContainer.getLogServices(); 558 } 559 getExtensionInstaller()560 public FrameworkExtensionInstaller getExtensionInstaller() { 561 return extensionInstaller; 562 } 563 isReadOnly()564 public boolean isReadOnly() { 565 return osgiLocation.isReadOnly(); 566 } 567 getContentConnection(Module module, String bundleLocation, final InputStream in)568 public URLConnection getContentConnection(Module module, String bundleLocation, final InputStream in) throws IOException { 569 List<StorageHookFactory<?, ?, ?>> storageHooks = getConfiguration().getHookRegistry().getStorageHookFactories(); 570 for (StorageHookFactory<?, ?, ?> storageHook : storageHooks) { 571 URLConnection hookContent = storageHook.handleContentConnection(module, bundleLocation, in); 572 if (hookContent != null) { 573 return hookContent; 574 } 575 } 576 577 if (in != null) { 578 return new URLConnection(null) { 579 /** 580 * @throws IOException 581 */ 582 @Override 583 public void connect() throws IOException { 584 connected = true; 585 } 586 587 /** 588 * @throws IOException 589 */ 590 @Override 591 public InputStream getInputStream() throws IOException { 592 return (in); 593 } 594 }; 595 } 596 if (module == null) { 597 if (bundleLocation == null) { 598 throw new IllegalArgumentException("Module and location cannot be null"); //$NON-NLS-1$ 599 } 600 return getContentConnection(bundleLocation); 601 } 602 return getContentConnection(getUpdateLocation(module)); 603 } 604 605 private String getUpdateLocation(final Module module) { 606 if (System.getSecurityManager() == null) 607 return getUpdateLocation0(module); 608 return AccessController.doPrivileged(new PrivilegedAction<String>() { 609 @Override 610 public String run() { 611 return getUpdateLocation0(module); 612 } 613 }); 614 } 615 616 String getUpdateLocation0(Module module) { 617 ModuleRevision current = module.getCurrentRevision(); 618 Generation generation = (Generation) current.getRevisionInfo(); 619 String updateLocation = generation.getHeaders().get(Constants.BUNDLE_UPDATELOCATION); 620 if (updateLocation == null) { 621 updateLocation = module.getLocation(); 622 } 623 if (updateLocation.startsWith(INITIAL_LOCATION)) { 624 updateLocation = updateLocation.substring(INITIAL_LOCATION.length()); 625 } 626 return updateLocation; 627 } 628 629 private URLConnection getContentConnection(final String spec) throws IOException { 630 if (System.getSecurityManager() == null) { 631 return LocationHelper.getConnection(createURL(spec)); 632 } 633 try { 634 return AccessController.doPrivileged(new PrivilegedExceptionAction<URLConnection>() { 635 @Override 636 public URLConnection run() throws IOException { 637 return LocationHelper.getConnection(createURL(spec)); 638 } 639 }); 640 } catch (PrivilegedActionException e) { 641 if (e.getException() instanceof IOException) 642 throw (IOException) e.getException(); 643 throw (RuntimeException) e.getException(); 644 } 645 } 646 647 URL createURL(String spec) throws MalformedURLException { 648 if (spec.startsWith(URLStreamHandlerFactoryImpl.PROTOCOL_REFERENCE)) { 649 return new URL(null, spec, new Handler(equinoxContainer.getConfiguration().getConfiguration(EquinoxLocations.PROP_INSTALL_AREA))); 650 } 651 return new URL(spec); 652 } 653 654 public Generation install(Module origin, String bundleLocation, URLConnection content) throws BundleException { 655 if (osgiLocation.isReadOnly()) { 656 throw new BundleException("The framework storage area is read only.", BundleException.INVALID_OPERATION); //$NON-NLS-1$ 657 } 658 URL sourceURL = content.getURL(); 659 InputStream in; 660 try { 661 in = content.getInputStream(); 662 } catch (Throwable e) { 663 throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ 664 } 665 666 // Check if the bundle already exists at this location 667 // before doing the staging and generation creation. 668 // This is important since some installers seem to continually 669 // re-install bundles using the same location each startup 670 Module existingLocation = moduleContainer.getModule(bundleLocation); 671 if (existingLocation != null) { 672 // NOTE this same logic is also in the ModuleContainer 673 // This is necessary because the container does the location locking. 674 // Another thread could win the location lock and install before this thread does. 675 try { 676 in.close(); 677 } catch (IOException e) { 678 // ignore 679 } 680 if (origin != null) { 681 // Check that the existing location is visible from the origin module 682 Bundle bundle = origin.getBundle(); 683 BundleContext context = bundle == null ? null : bundle.getBundleContext(); 684 if (context != null && context.getBundle(existingLocation.getId()) == null) { 685 Bundle b = existingLocation.getBundle(); 686 throw new BundleException(NLS.bind(Msg.ModuleContainer_NameCollisionWithLocation, new Object[] {b.getSymbolicName(), b.getVersion(), bundleLocation}), BundleException.REJECTED_BY_HOOK); 687 } 688 } 689 return (Generation) existingLocation.getCurrentRevision().getRevisionInfo(); 690 } 691 692 boolean isReference = in instanceof ReferenceInputStream; 693 File staged = stageContent(in, sourceURL); 694 Generation generation = null; 695 try { 696 Long nextID = moduleDatabase.getAndIncrementNextId(); 697 BundleInfo info = new BundleInfo(this, nextID, bundleLocation, 0); 698 generation = info.createGeneration(); 699 700 File contentFile = getContentFile(staged, isReference, nextID, generation.getGenerationId()); 701 generation.setContent(contentFile, isReference); 702 // Check that we can open the bundle file 703 generation.getBundleFile().open(); 704 setStorageHooks(generation); 705 706 ModuleRevisionBuilder builder = getBuilder(generation); 707 builder.setId(nextID); 708 709 Module m = moduleContainer.install(origin, bundleLocation, builder, generation); 710 if (!nextID.equals(m.getId())) { 711 // this revision is already installed. delete the generation 712 generation.delete(); 713 return (Generation) m.getCurrentRevision().getRevisionInfo(); 714 } 715 return generation; 716 } catch (Throwable t) { 717 if (!isReference) { 718 try { 719 delete(staged); 720 } catch (IOException e) { 721 // tried our best 722 } 723 } 724 if (generation != null) { 725 generation.delete(); 726 generation.getBundleInfo().delete(); 727 } 728 if (t instanceof SecurityException) { 729 // TODO hack from ModuleContainer 730 // if the cause is a bundle exception then throw that 731 if (t.getCause() instanceof BundleException) { 732 throw (BundleException) t.getCause(); 733 } 734 throw (SecurityException) t; 735 } 736 if (t instanceof BundleException) { 737 throw (BundleException) t; 738 } 739 throw new BundleException("Error occurred installing a bundle.", t); //$NON-NLS-1$ 740 } finally { 741 if (generation != null) { 742 generation.getBundleInfo().unlockGeneration(generation); 743 } 744 } 745 } 746 747 private void setStorageHooks(Generation generation) throws BundleException { 748 if (generation.getBundleInfo().getBundleId() == 0) { 749 return; // ignore system bundle 750 } 751 List<StorageHookFactory<?, ?, ?>> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); 752 List<StorageHook<?, ?>> hooks = new ArrayList<>(factories.size()); 753 for (Iterator<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { 754 @SuppressWarnings("unchecked") 755 StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) iFactories.next(); 756 StorageHook<Object, Object> hook = next.createStorageHookAndValidateFactoryClass(generation); 757 if (hook != null) { 758 hooks.add(hook); 759 } 760 } 761 generation.setStorageHooks(Collections.unmodifiableList(hooks), true); 762 for (StorageHook<?, ?> hook : hooks) { 763 hook.initialize(generation.getHeaders()); 764 } 765 } 766 767 public ModuleRevisionBuilder getBuilder(Generation generation) throws BundleException { 768 return getBuilder(generation, null, null); 769 } 770 771 public ModuleRevisionBuilder getBuilder(Generation generation, String extraCapabilities, String extraExports) throws BundleException { 772 Dictionary<String, String> headers = generation.getHeaders(); 773 Map<String, String> mapHeaders; 774 if (headers instanceof Map) { 775 @SuppressWarnings("unchecked") 776 Map<String, String> unchecked = (Map<String, String>) headers; 777 mapHeaders = unchecked; 778 } else { 779 mapHeaders = new HashMap<>(); 780 for (Enumeration<String> eKeys = headers.keys(); eKeys.hasMoreElements();) { 781 String key = eKeys.nextElement(); 782 mapHeaders.put(key, headers.get(key)); 783 } 784 } 785 if (generation.getBundleInfo().getBundleId() != 0) { 786 ModuleRevisionBuilder builder = allowRestrictedProvides ? OSGiManifestBuilderFactory.createBuilder(mapHeaders, null, null, "") : OSGiManifestBuilderFactory.createBuilder(mapHeaders); //$NON-NLS-1$ 787 if ((builder.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) { 788 for (ModuleRevisionBuilder.GenericInfo reqInfo : builder.getRequirements()) { 789 if (HostNamespace.HOST_NAMESPACE.equals(reqInfo.getNamespace())) { 790 if (HostNamespace.EXTENSION_BOOTCLASSPATH.equals(reqInfo.getDirectives().get(HostNamespace.REQUIREMENT_EXTENSION_DIRECTIVE))) { 791 throw new BundleException("Boot classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$ 792 } 793 } 794 } 795 } 796 return builder; 797 } 798 799 return OSGiManifestBuilderFactory.createBuilder(mapHeaders, Constants.SYSTEM_BUNDLE_SYMBOLICNAME, extraExports, extraCapabilities); 800 } 801 802 private String getSystemExtraCapabilities() { 803 EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); 804 StringBuilder result = new StringBuilder(); 805 806 String systemCapabilities = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES); 807 if (systemCapabilities != null && systemCapabilities.trim().length() > 0) { 808 result.append(systemCapabilities).append(", "); //$NON-NLS-1$ 809 } 810 811 String extraSystemCapabilities = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA); 812 if (extraSystemCapabilities != null && extraSystemCapabilities.trim().length() > 0) { 813 result.append(extraSystemCapabilities).append(", "); //$NON-NLS-1$ 814 } 815 816 result.append(EclipsePlatformNamespace.ECLIPSE_PLATFORM_NAMESPACE).append("; "); //$NON-NLS-1$ 817 result.append(EquinoxConfiguration.PROP_OSGI_OS).append("=").append(equinoxConfig.getOS()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ 818 result.append(EquinoxConfiguration.PROP_OSGI_WS).append("=").append(equinoxConfig.getWS()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ 819 result.append(EquinoxConfiguration.PROP_OSGI_ARCH).append("=").append(equinoxConfig.getOSArch()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ 820 result.append(EquinoxConfiguration.PROP_OSGI_NL).append("=").append(equinoxConfig.getNL()); //$NON-NLS-1$ 821 822 String osName = equinoxConfig.getConfiguration(Constants.FRAMEWORK_OS_NAME); 823 osName = osName == null ? null : osName.toLowerCase(); 824 String processor = equinoxConfig.getConfiguration(Constants.FRAMEWORK_PROCESSOR); 825 processor = processor == null ? null : processor.toLowerCase(); 826 String osVersion = equinoxConfig.getConfiguration(Constants.FRAMEWORK_OS_VERSION); 827 osVersion = osVersion == null ? null : osVersion.toLowerCase(); 828 String language = equinoxConfig.getConfiguration(Constants.FRAMEWORK_LANGUAGE); 829 language = language == null ? null : language.toLowerCase(); 830 831 result.append(", "); //$NON-NLS-1$ 832 result.append(NativeNamespace.NATIVE_NAMESPACE).append("; "); //$NON-NLS-1$ 833 if (osName != null) { 834 osName = getAliasList(equinoxConfig.getAliasMapper().getOSNameAliases(osName)); 835 result.append(NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE).append(":List<String>=").append(osName).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ 836 } 837 if (processor != null) { 838 processor = getAliasList(equinoxConfig.getAliasMapper().getProcessorAliases(processor)); 839 result.append(NativeNamespace.CAPABILITY_PROCESSOR_ATTRIBUTE).append(":List<String>=").append(processor).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ 840 } 841 result.append(NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE).append(":Version").append("=\"").append(osVersion).append("\"; "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 842 result.append(NativeNamespace.CAPABILITY_LANGUAGE_ATTRIBUTE).append("=\"").append(language).append('\"'); //$NON-NLS-1$ 843 return result.toString(); 844 } 845 846 String getAliasList(Collection<String> aliases) { 847 if (aliases.isEmpty()) { 848 return null; 849 } 850 StringBuilder builder = new StringBuilder(); 851 builder.append('"'); 852 for (String alias : aliases) { 853 builder.append(alias).append(','); 854 } 855 builder.setLength(builder.length() - 1); 856 builder.append('"'); 857 return builder.toString(); 858 } 859 860 private String getSystemExtraPackages() { 861 EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); 862 StringBuilder result = new StringBuilder(); 863 864 String systemPackages = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES); 865 if (systemPackages != null) { 866 result.append(systemPackages); 867 } 868 869 String extraSystemPackages = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); 870 if (extraSystemPackages != null && extraSystemPackages.trim().length() > 0) { 871 if (result.length() > 0) { 872 result.append(", "); //$NON-NLS-1$ 873 } 874 result.append(extraSystemPackages); 875 } 876 877 return result.toString(); 878 } 879 880 private void refresh(Module module) throws BundleException { 881 ModuleRevision current = module.getCurrentRevision(); 882 Generation currentGen = (Generation) current.getRevisionInfo(); 883 File content = currentGen.getContent(); 884 String spec = (currentGen.isReference() ? "reference:" : "") + content.toURI().toString(); //$NON-NLS-1$ //$NON-NLS-2$ 885 URLConnection contentConn; 886 try { 887 contentConn = getContentConnection(spec); 888 } catch (IOException e) { 889 throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ 890 } 891 update(module, contentConn); 892 } 893 894 public Generation update(Module module, URLConnection content) throws BundleException { 895 if (osgiLocation.isReadOnly()) { 896 throw new BundleException("The framework storage area is read only.", BundleException.INVALID_OPERATION); //$NON-NLS-1$ 897 } 898 URL sourceURL = content.getURL(); 899 InputStream in; 900 try { 901 in = content.getInputStream(); 902 } catch (Throwable e) { 903 throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ 904 } 905 boolean isReference = in instanceof ReferenceInputStream; 906 File staged = stageContent(in, sourceURL); 907 ModuleRevision current = module.getCurrentRevision(); 908 Generation currentGen = (Generation) current.getRevisionInfo(); 909 910 BundleInfo bundleInfo = currentGen.getBundleInfo(); 911 Generation newGen = bundleInfo.createGeneration(); 912 913 try { 914 File contentFile = getContentFile(staged, isReference, bundleInfo.getBundleId(), newGen.getGenerationId()); 915 newGen.setContent(contentFile, isReference); 916 // Check that we can open the bundle file 917 newGen.getBundleFile().open(); 918 setStorageHooks(newGen); 919 920 ModuleRevisionBuilder builder = getBuilder(newGen); 921 moduleContainer.update(module, builder, newGen); 922 } catch (Throwable t) { 923 if (!isReference) { 924 try { 925 delete(staged); 926 } catch (IOException e) { 927 // tried our best 928 } 929 } 930 newGen.delete(); 931 if (t instanceof SecurityException) { 932 // TODO hack from ModuleContainer 933 // if the cause is a bundle exception then throw that 934 if (t.getCause() instanceof BundleException) { 935 throw (BundleException) t.getCause(); 936 } 937 throw (SecurityException) t; 938 } 939 if (t instanceof BundleException) { 940 throw (BundleException) t; 941 } 942 throw new BundleException("Error occurred updating a bundle.", t); //$NON-NLS-1$ 943 } finally { 944 bundleInfo.unlockGeneration(newGen); 945 } 946 return newGen; 947 } 948 949 private File getContentFile(final File staged, final boolean isReference, final long bundleID, final long generationID) throws BundleException { 950 if (System.getSecurityManager() == null) 951 return getContentFile0(staged, isReference, bundleID, generationID); 952 try { 953 return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() { 954 @Override 955 public File run() throws BundleException { 956 return getContentFile0(staged, isReference, bundleID, generationID); 957 } 958 }); 959 } catch (PrivilegedActionException e) { 960 if (e.getException() instanceof BundleException) 961 throw (BundleException) e.getException(); 962 throw (RuntimeException) e.getException(); 963 } 964 } 965 966 File getContentFile0(File staged, boolean isReference, long bundleID, long generationID) throws BundleException { 967 File contentFile; 968 if (!isReference) { 969 File generationRoot = new File(childRoot, bundleID + "/" + generationID); //$NON-NLS-1$ 970 generationRoot.mkdirs(); 971 if (!generationRoot.isDirectory()) { 972 throw new BundleException("Could not create generation directory: " + generationRoot.getAbsolutePath()); //$NON-NLS-1$ 973 } 974 contentFile = new File(generationRoot, BUNDLE_FILE_NAME); 975 try { 976 StorageUtil.move(staged, contentFile, getConfiguration().getDebug().DEBUG_STORAGE); 977 } catch (IOException e) { 978 throw new BundleException("Error while renaming bundle file to final location: " + contentFile, //$NON-NLS-1$ 979 BundleException.READ_ERROR, e); 980 } 981 } else { 982 contentFile = staged; 983 } 984 return contentFile; 985 } 986 987 private static String getBundleFilePath(long bundleID, long generationID) { 988 return bundleID + "/" + generationID + "/" + BUNDLE_FILE_NAME; //$NON-NLS-1$ //$NON-NLS-2$ 989 } 990 991 /** 992 * Gets a file from storage and conditionally checks the parent storage area 993 * if the file does not exist in the child configuration. 994 * Note, this method does not check for escaping of paths from the root storage area. 995 * @param path the path relative to the root of the storage area 996 * @param checkParent if true then check the parent storage (if any) when the file 997 * does not exist in the child storage area 998 * @return the file being requested. A {@code null} value is never returned. The file 999 * returned may not exist. 1000 * @throws StorageException if there was an issue getting the file 1001 */ 1002 public File getFile(String path, boolean checkParent) throws StorageException { 1003 return getFile(null, path, checkParent); 1004 } 1005 1006 /** 1007 * Same as {@link #getFile(String, boolean)} except takes a base parameter which is 1008 * appended to the root storage area before looking for the path. If base is not 1009 * null then additional checks are done to make sure the path does not escape out 1010 * of the base path. 1011 * @param base the additional base path to append to the root storage area. May be 1012 * {@code null}, in which case no check is done for escaping out of the base path. 1013 * @param path the path relative to the root + base storage area. 1014 * @param checkParent if true then check the parent storage (if any) when the file 1015 * does not exist in the child storage area 1016 * @return the file being requested. A {@code null} value is never returned. The file 1017 * returned may not exist. 1018 * @throws StorageException if there was an issue getting the file 1019 */ 1020 public File getFile(String base, String path, boolean checkParent) throws StorageException { 1021 // first check the child location 1022 File childPath = getFile(childRoot, base, path); 1023 // now check the parent 1024 if (checkParent && parentRoot != null) { 1025 if (childPath.exists()) { 1026 return childPath; 1027 } 1028 File parentPath = getFile(parentRoot, base, path); 1029 if (parentPath.exists()) { 1030 // only use the parent file only if it exists; 1031 return parentPath; 1032 } 1033 } 1034 // did not exist in both locations; use the child path 1035 return childPath; 1036 } 1037 1038 private static File getFile(File root, String base, String path) { 1039 if (base == null) { 1040 // return quick; no need to check for path traversal 1041 return new File(root, path); 1042 } 1043 1044 // if base is not null then move root to include the base 1045 root = new File(root, base); 1046 File result = new File(root, path); 1047 1048 // do the extra check to make sure the path did not escape the root path 1049 try { 1050 String resultCanonical = result.getCanonicalPath(); 1051 String rootCanonical = root.getCanonicalPath(); 1052 if (!resultCanonical.startsWith(rootCanonical + File.separator) && !resultCanonical.equals(rootCanonical)) { 1053 throw new StorageException("Invalid path: " + path); //$NON-NLS-1$ 1054 } 1055 } catch (IOException e) { 1056 throw new StorageException("Invalid path: " + path, e); //$NON-NLS-1$ 1057 } 1058 return result; 1059 } 1060 1061 private File stageContent(final InputStream in, final URL sourceURL) throws BundleException { 1062 if (System.getSecurityManager() == null) 1063 return stageContent0(in, sourceURL); 1064 try { 1065 return AccessController.doPrivileged(new PrivilegedExceptionAction<File>() { 1066 @Override 1067 public File run() throws BundleException { 1068 return stageContent0(in, sourceURL); 1069 } 1070 }); 1071 } catch (PrivilegedActionException e) { 1072 if (e.getException() instanceof BundleException) 1073 throw (BundleException) e.getException(); 1074 throw (RuntimeException) e.getException(); 1075 } 1076 } 1077 1078 File stageContent0(InputStream in, URL sourceURL) throws BundleException { 1079 File outFile = null; 1080 try { 1081 if (in instanceof ReferenceInputStream) { 1082 return ((ReferenceInputStream) in).getReference(); 1083 } 1084 1085 outFile = File.createTempFile(BUNDLE_FILE_NAME, ".tmp", childRoot); //$NON-NLS-1$ 1086 String protocol = sourceURL == null ? null : sourceURL.getProtocol(); 1087 1088 if ("file".equals(protocol)) { //$NON-NLS-1$ 1089 File inFile = new File(sourceURL.getPath()); 1090 inFile = LocationHelper.decodePath(inFile); 1091 if (inFile.isDirectory()) { 1092 // need to delete the outFile because it is not a directory 1093 outFile.delete(); 1094 StorageUtil.copyDir(inFile, outFile); 1095 } else { 1096 StorageUtil.readFile(in, outFile); 1097 } 1098 } else { 1099 StorageUtil.readFile(in, outFile); 1100 } 1101 return outFile; 1102 } catch (IOException e) { 1103 if (outFile != null) { 1104 outFile.delete(); 1105 } 1106 throw new BundleException(Msg.BUNDLE_READ_EXCEPTION, BundleException.READ_ERROR, e); 1107 } 1108 } 1109 1110 /** 1111 * Attempts to set the permissions of the file in a system dependent way. 1112 * @param file the file to set the permissions on 1113 */ 1114 public void setPermissions(File file) { 1115 String commandProp = getConfiguration().getConfiguration(EquinoxConfiguration.PROP_SETPERMS_CMD); 1116 if (commandProp == null) 1117 commandProp = getConfiguration().getConfiguration(Constants.FRAMEWORK_EXECPERMISSION); 1118 if (commandProp == null) 1119 return; 1120 String[] commandComponents = ManifestElement.getArrayFromList(commandProp, " "); //$NON-NLS-1$ 1121 List<String> command = new ArrayList<>(commandComponents.length + 1); 1122 boolean foundFullPath = false; 1123 for (String commandComponent : commandComponents) { 1124 if ("[fullpath]".equals(commandComponent) || "${abspath}".equals(commandComponent)) { //$NON-NLS-1$ //$NON-NLS-2$ 1125 command.add(file.getAbsolutePath()); 1126 foundFullPath = true; 1127 } else { 1128 command.add(commandComponent); 1129 } 1130 } 1131 if (!foundFullPath) 1132 command.add(file.getAbsolutePath()); 1133 try { 1134 Runtime.getRuntime().exec(command.toArray(new String[command.size()])).waitFor(); 1135 } catch (Exception e) { 1136 e.printStackTrace(); 1137 } 1138 } 1139 1140 public BundleFile createBundleFile(File content, Generation generation, boolean isDirectory, boolean isBase) { 1141 BundleFile result; 1142 try { 1143 if (isDirectory) { 1144 boolean strictPath = Boolean.parseBoolean(equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROPERTY_STRICT_BUNDLE_ENTRY_PATH, Boolean.FALSE.toString())); 1145 result = new DirBundleFile(content, strictPath); 1146 } else { 1147 result = new ZipBundleFile(content, generation, mruList, getConfiguration().getDebug()); 1148 } 1149 } catch (IOException e) { 1150 throw new RuntimeException("Could not create bundle file.", e); //$NON-NLS-1$ 1151 } 1152 return wrapBundleFile(result, generation, isBase); 1153 } 1154 1155 public BundleFile createNestedBundleFile(String nestedDir, BundleFile bundleFile, Generation generation) { 1156 return createNestedBundleFile(nestedDir, bundleFile, generation, Collections.<String> emptyList()); 1157 } 1158 1159 public BundleFile createNestedBundleFile(String nestedDir, BundleFile bundleFile, Generation generation, Collection<String> filterPrefixes) { 1160 // here we assume the content is a path offset into the base bundle file; create a NestedDirBundleFile 1161 return wrapBundleFile(new NestedDirBundleFile(bundleFile, nestedDir, filterPrefixes), generation, false); 1162 } 1163 1164 public BundleFile wrapBundleFile(BundleFile bundleFile, Generation generation, boolean isBase) { 1165 // try creating a wrapper bundlefile out of it. 1166 List<BundleFileWrapperFactoryHook> wrapperFactories = getConfiguration().getHookRegistry().getBundleFileWrapperFactoryHooks(); 1167 BundleFileWrapperChain wrapped = wrapperFactories.isEmpty() ? null : new BundleFileWrapperChain(bundleFile, null); 1168 for (BundleFileWrapperFactoryHook wrapperFactory : wrapperFactories) { 1169 BundleFileWrapper wrapperBundle = wrapperFactory.wrapBundleFile(bundleFile, generation, isBase); 1170 if (wrapperBundle != null && wrapperBundle != bundleFile) 1171 bundleFile = wrapped = new BundleFileWrapperChain(wrapperBundle, wrapped); 1172 } 1173 1174 return bundleFile; 1175 } 1176 1177 public void compact() { 1178 if (!osgiLocation.isReadOnly()) { 1179 compact(childRoot); 1180 } 1181 } 1182 1183 private void compact(File directory) { 1184 if (getConfiguration().getDebug().DEBUG_STORAGE) 1185 Debug.println("compact(" + directory.getPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ 1186 String list[] = directory.list(); 1187 if (list == null) 1188 return; 1189 1190 int len = list.length; 1191 for (int i = 0; i < len; i++) { 1192 if (BUNDLE_DATA_DIR.equals(list[i])) 1193 continue; /* do not examine the bundles data dir. */ 1194 File target = new File(directory, list[i]); 1195 // if the file is a directory 1196 if (!target.isDirectory()) 1197 continue; 1198 File delete = new File(target, DELETE_FLAG); 1199 // and the directory is marked for delete 1200 if (delete.exists()) { 1201 // if rm fails to delete the directory and .delete was removed 1202 if (!StorageUtil.rm(target, getConfiguration().getDebug().DEBUG_STORAGE) && !delete.exists()) { 1203 try { 1204 // recreate .delete 1205 FileOutputStream out = new FileOutputStream(delete); 1206 out.close(); 1207 } catch (IOException e) { 1208 if (getConfiguration().getDebug().DEBUG_STORAGE) 1209 Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ 1210 } 1211 } 1212 } else { 1213 compact(target); /* descend into directory */ 1214 } 1215 } 1216 } 1217 1218 void delete(final File delete) throws IOException { 1219 if (System.getSecurityManager() == null) { 1220 delete0(delete); 1221 } else { 1222 try { 1223 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 1224 @Override 1225 public Void run() throws IOException { 1226 delete0(delete); 1227 return null; 1228 } 1229 }); 1230 } catch (PrivilegedActionException e) { 1231 if (e.getException() instanceof IOException) 1232 throw (IOException) e.getException(); 1233 throw (RuntimeException) e.getException(); 1234 } 1235 } 1236 } 1237 1238 void delete0(File delete) throws IOException { 1239 if (!StorageUtil.rm(delete, getConfiguration().getDebug().DEBUG_STORAGE)) { 1240 /* create .delete */ 1241 FileOutputStream out = new FileOutputStream(new File(delete, DELETE_FLAG)); 1242 out.close(); 1243 } 1244 } 1245 1246 public void save() throws IOException { 1247 if (isReadOnly()) { 1248 return; 1249 } 1250 if (System.getSecurityManager() == null) { 1251 save0(); 1252 } else { 1253 try { 1254 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 1255 @Override 1256 public Void run() throws IOException { 1257 save0(); 1258 return null; 1259 } 1260 }); 1261 } catch (PrivilegedActionException e) { 1262 if (e.getException() instanceof IOException) 1263 throw (IOException) e.getException(); 1264 throw (RuntimeException) e.getException(); 1265 } 1266 } 1267 } 1268 1269 void save0() throws IOException { 1270 StorageManager childStorageManager = null; 1271 ManagedOutputStream mos = null; 1272 DataOutputStream out = null; 1273 boolean success = false; 1274 moduleDatabase.readLock(); 1275 try { 1276 synchronized (this.saveMonitor) { 1277 if (lastSavedTimestamp == moduleDatabase.getTimestamp()) 1278 return; 1279 childStorageManager = getChildStorageManager(); 1280 mos = childStorageManager.getOutputStream(FRAMEWORK_INFO); 1281 out = new DataOutputStream(new BufferedOutputStream(mos)); 1282 saveGenerations(out); 1283 savePermissionData(out); 1284 moduleDatabase.store(out, true); 1285 lastSavedTimestamp = moduleDatabase.getTimestamp(); 1286 success = true; 1287 } 1288 } finally { 1289 if (!success) { 1290 if (mos != null) { 1291 mos.abort(); 1292 } 1293 } 1294 if (out != null) { 1295 try { 1296 out.close(); 1297 } catch (IOException e) { 1298 // tried our best 1299 } 1300 } 1301 if (childStorageManager != null) { 1302 childStorageManager.close(); 1303 } 1304 moduleDatabase.readUnlock(); 1305 } 1306 } 1307 1308 private void savePermissionData(DataOutputStream out) throws IOException { 1309 permissionData.savePermissionData(out); 1310 } 1311 1312 private void saveGenerations(DataOutputStream out) throws IOException { 1313 List<Module> modules = moduleContainer.getModules(); 1314 List<Generation> generations = new ArrayList<>(); 1315 for (Module module : modules) { 1316 ModuleRevision revision = module.getCurrentRevision(); 1317 if (revision != null) { 1318 Generation generation = (Generation) revision.getRevisionInfo(); 1319 if (generation != null) { 1320 generations.add(generation); 1321 } 1322 } 1323 } 1324 out.writeInt(VERSION); 1325 1326 out.writeUTF(runtimeVersion.toString()); 1327 1328 Version curFrameworkVersion = findFrameworkVersion(); 1329 out.writeUTF(curFrameworkVersion == null ? Version.emptyVersion.toString() : curFrameworkVersion.toString()); 1330 1331 saveLongString(out, getSystemExtraCapabilities()); 1332 saveLongString(out, getSystemExtraPackages()); 1333 1334 out.writeInt(cachedHeaderKeys.size()); 1335 for (String headerKey : cachedHeaderKeys) { 1336 out.writeUTF(headerKey); 1337 } 1338 1339 out.writeInt(generations.size()); 1340 for (Generation generation : generations) { 1341 BundleInfo bundleInfo = generation.getBundleInfo(); 1342 out.writeLong(bundleInfo.getBundleId()); 1343 out.writeUTF(bundleInfo.getLocation()); 1344 out.writeLong(bundleInfo.getNextGenerationId()); 1345 out.writeLong(generation.getGenerationId()); 1346 out.writeBoolean(generation.isDirectory()); 1347 out.writeBoolean(generation.isReference()); 1348 out.writeBoolean(generation.hasPackageInfo()); 1349 if (bundleInfo.getBundleId() == 0) { 1350 // just write empty string for system bundle content in this case 1351 out.writeUTF(""); //$NON-NLS-1$ 1352 } else { 1353 if (generation.isReference()) { 1354 // make reference installs relative to the install path 1355 out.writeUTF(new FilePath(installPath).makeRelative(new FilePath(generation.getContent().getAbsolutePath()))); 1356 } else { 1357 // make normal installs relative to the storage area 1358 out.writeUTF(Storage.getBundleFilePath(bundleInfo.getBundleId(), generation.getGenerationId())); 1359 } 1360 } 1361 out.writeLong(generation.getLastModified()); 1362 1363 Dictionary<String, String> headers = generation.getHeaders(); 1364 for (String headerKey : cachedHeaderKeys) { 1365 String value = headers.get(headerKey); 1366 if (value != null) { 1367 out.writeUTF(value); 1368 } else { 1369 out.writeUTF(NUL); 1370 } 1371 } 1372 1373 out.writeBoolean(generation.isMRJar()); 1374 } 1375 1376 saveStorageHookData(out, generations); 1377 } 1378 1379 private void saveLongString(DataOutputStream out, String value) throws IOException { 1380 if (value == null) { 1381 out.writeInt(0); 1382 } else { 1383 // don't use out.writeUTF because it has a hard string limit 1384 byte[] data = value.getBytes(StandardCharsets.UTF_8); 1385 out.writeInt(data.length); 1386 out.write(data); 1387 } 1388 } 1389 1390 private String readLongString(DataInputStream in) throws IOException { 1391 int length = in.readInt(); 1392 byte[] data = new byte[length]; 1393 in.readFully(data); 1394 return new String(data, StandardCharsets.UTF_8); 1395 } 1396 1397 private void saveStorageHookData(DataOutputStream out, List<Generation> generations) throws IOException { 1398 List<StorageHookFactory<?, ?, ?>> factories = getConfiguration().getHookRegistry().getStorageHookFactories(); 1399 out.writeInt(factories.size()); 1400 for (StorageHookFactory<?, ?, ?> factory : factories) { 1401 out.writeUTF(factory.getKey()); 1402 out.writeInt(factory.getStorageVersion()); 1403 1404 // create a temporary in memory stream so we can figure out the length 1405 ByteArrayOutputStream tempBytes = new ByteArrayOutputStream(); 1406 DataOutputStream temp = new DataOutputStream(tempBytes); 1407 try { 1408 Object saveContext = factory.createSaveContext(); 1409 for (Generation generation : generations) { 1410 if (generation.getBundleInfo().getBundleId() == 0) { 1411 continue; // ignore system bundle 1412 } 1413 @SuppressWarnings({"rawtypes", "unchecked"}) 1414 StorageHook<Object, Object> hook = generation.getStorageHook((Class) factory.getClass()); 1415 if (hook != null) { 1416 hook.save(saveContext, temp); 1417 } 1418 } 1419 } finally { 1420 temp.close(); 1421 } 1422 out.writeInt(tempBytes.size()); 1423 out.write(tempBytes.toByteArray()); 1424 } 1425 } 1426 1427 private Map<Long, Generation> loadGenerations(DataInputStream in, String[] cachedInfo) throws IOException { 1428 if (in == null) { 1429 return new HashMap<>(0); 1430 } 1431 int version = in.readInt(); 1432 if (version > VERSION || version < LOWEST_VERSION_SUPPORTED) { 1433 throw new IllegalArgumentException("Found persistent version \"" + version + "\" expecting \"" + VERSION + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 1434 } 1435 Version savedRuntimeVersion = (version >= MR_JAR_VERSION) ? Version.parseVersion(in.readUTF()) : null; 1436 if (savedRuntimeVersion == null || !savedRuntimeVersion.equals(runtimeVersion)) { 1437 refreshMRBundles.set(true); 1438 } 1439 1440 cachedInfo[0] = (version >= CACHED_SYSTEM_CAPS_VERION) ? in.readUTF() : null; 1441 cachedInfo[1] = (version >= CACHED_SYSTEM_CAPS_VERION) ? readLongString(in) : null; 1442 cachedInfo[2] = (version >= CACHED_SYSTEM_CAPS_VERION) ? readLongString(in) : null; 1443 1444 int numCachedHeaders = in.readInt(); 1445 List<String> storedCachedHeaderKeys = new ArrayList<>(numCachedHeaders); 1446 for (int i = 0; i < numCachedHeaders; i++) { 1447 storedCachedHeaderKeys.add(ObjectPool.intern(in.readUTF())); 1448 } 1449 1450 int numInfos = in.readInt(); 1451 Map<Long, Generation> result = new HashMap<>(numInfos); 1452 List<Generation> generations = new ArrayList<>(numInfos); 1453 for (int i = 0; i < numInfos; i++) { 1454 long infoId = in.readLong(); 1455 String infoLocation = ObjectPool.intern(in.readUTF()); 1456 long nextGenId = in.readLong(); 1457 long generationId = in.readLong(); 1458 boolean isDirectory = in.readBoolean(); 1459 boolean isReference = in.readBoolean(); 1460 boolean hasPackageInfo = in.readBoolean(); 1461 String contentPath = in.readUTF(); 1462 long lastModified = in.readLong(); 1463 1464 Map<String, String> cachedHeaders = new HashMap<>(storedCachedHeaderKeys.size()); 1465 for (String headerKey : storedCachedHeaderKeys) { 1466 String value = in.readUTF(); 1467 if (NUL.equals(value)) { 1468 value = null; 1469 } else { 1470 value = ObjectPool.intern(value); 1471 } 1472 cachedHeaders.put(headerKey, value); 1473 } 1474 boolean isMRJar = (version >= MR_JAR_VERSION) ? in.readBoolean() : false; 1475 1476 File content; 1477 if (infoId == 0) { 1478 content = getSystemContent(); 1479 isDirectory = content != null ? content.isDirectory() : false; 1480 // Note that we do not do any checking for absolute paths with 1481 // the system bundle. We always take the content as discovered 1482 // by getSystemContent() 1483 } else { 1484 content = new File(contentPath); 1485 if (!content.isAbsolute()) { 1486 // make sure it has the absolute location instead 1487 if (isReference) { 1488 // reference installs are relative to the installPath 1489 content = new File(installPath, contentPath); 1490 } else { 1491 // normal installs are relative to the storage area 1492 content = getFile(contentPath, true); 1493 } 1494 } 1495 } 1496 1497 BundleInfo info = new BundleInfo(this, infoId, infoLocation, nextGenId); 1498 Generation generation = info.restoreGeneration(generationId, content, isDirectory, isReference, hasPackageInfo, cachedHeaders, lastModified, isMRJar); 1499 result.put(infoId, generation); 1500 generations.add(generation); 1501 } 1502 1503 loadStorageHookData(generations, in); 1504 return result; 1505 } 1506 1507 private void loadStorageHookData(List<Generation> generations, DataInputStream in) throws IOException { 1508 List<StorageHookFactory<?, ?, ?>> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); 1509 Map<Generation, List<StorageHook<?, ?>>> hookMap = new HashMap<>(); 1510 int numFactories = in.readInt(); 1511 for (int i = 0; i < numFactories; i++) { 1512 String factoryName = in.readUTF(); 1513 int version = in.readInt(); 1514 StorageHookFactory<Object, Object, StorageHook<Object, Object>> factory = null; 1515 for (Iterator<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { 1516 @SuppressWarnings("unchecked") 1517 StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) iFactories.next(); 1518 if (next.getKey().equals(factoryName)) { 1519 factory = next; 1520 iFactories.remove(); 1521 break; 1522 } 1523 } 1524 int dataSize = in.readInt(); 1525 byte[] bytes = new byte[dataSize]; 1526 in.readFully(bytes); 1527 if (factory != null) { 1528 DataInputStream temp = new DataInputStream(new ByteArrayInputStream(bytes)); 1529 try { 1530 if (factory.isCompatibleWith(version)) { 1531 Object loadContext = factory.createLoadContext(version); 1532 for (Generation generation : generations) { 1533 if (generation.getBundleInfo().getBundleId() == 0) { 1534 continue; // ignore system bundle 1535 } 1536 StorageHook<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); 1537 if (hook != null) { 1538 hook.load(loadContext, temp); 1539 getHooks(hookMap, generation).add(hook); 1540 } 1541 } 1542 } else { 1543 // recover by reinitializing the hook 1544 for (Generation generation : generations) { 1545 if (generation.getBundleInfo().getBundleId() == 0) { 1546 continue; // ignore system bundle 1547 } 1548 StorageHook<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); 1549 if (hook != null) { 1550 hook.initialize(generation.getHeaders()); 1551 getHooks(hookMap, generation).add(hook); 1552 } 1553 } 1554 } 1555 } catch (BundleException e) { 1556 throw new IOException(e); 1557 } finally { 1558 temp.close(); 1559 } 1560 } 1561 } 1562 // now we need to recover for any hooks that are left 1563 for (Iterator<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { 1564 @SuppressWarnings("unchecked") 1565 StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) iFactories.next(); 1566 // recover by reinitializing the hook 1567 for (Generation generation : generations) { 1568 if (generation.getBundleInfo().getBundleId() == 0) { 1569 continue; // ignore system bundle 1570 } 1571 StorageHook<Object, Object> hook = next.createStorageHookAndValidateFactoryClass(generation); 1572 if (hook != null) { 1573 try { 1574 hook.initialize(generation.getHeaders()); 1575 getHooks(hookMap, generation).add(hook); 1576 } catch (BundleException e) { 1577 throw new IOException(e); 1578 } 1579 } 1580 } 1581 } 1582 // now set the hooks to the generations 1583 for (Generation generation : generations) { 1584 generation.setStorageHooks(Collections.unmodifiableList(getHooks(hookMap, generation)), false); 1585 } 1586 } 1587 1588 private static List<StorageHook<?, ?>> getHooks(Map<Generation, List<StorageHook<?, ?>>> hookMap, Generation generation) { 1589 List<StorageHook<?, ?>> result = hookMap.get(generation); 1590 if (result == null) { 1591 result = new ArrayList<>(); 1592 hookMap.put(generation, result); 1593 } 1594 return result; 1595 } 1596 1597 private File getSystemContent() { 1598 String frameworkValue = equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_FRAMEWORK); 1599 if (frameworkValue == null || !frameworkValue.startsWith("file:")) { //$NON-NLS-1$ 1600 return null; 1601 } 1602 // TODO assumes the location is a file URL 1603 File result = new File(frameworkValue.substring(5)).getAbsoluteFile(); 1604 if (!result.exists()) { 1605 throw new IllegalStateException("Configured framework location does not exist: " + result.getAbsolutePath()); //$NON-NLS-1$ 1606 } 1607 return result; 1608 } 1609 1610 @SuppressWarnings("deprecation") 1611 private void loadVMProfile(Generation systemGeneration) { 1612 EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); 1613 Properties profileProps = findVMProfile(systemGeneration); 1614 String systemExports = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES); 1615 // set the system exports property using the vm profile; only if the property is not already set 1616 if (systemExports == null) { 1617 systemExports = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); 1618 if (systemExports != null) 1619 equinoxConfig.setConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES, systemExports); 1620 } 1621 1622 // set the org.osgi.framework.bootdelegation property according to the java profile 1623 String type = equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_BOOTDELEGATION); // a null value means ignore 1624 String profileBootDelegation = profileProps.getProperty(Constants.FRAMEWORK_BOOTDELEGATION); 1625 if (EquinoxConfiguration.PROP_OSGI_BOOTDELEGATION_OVERRIDE.equals(type)) { 1626 if (profileBootDelegation == null) 1627 equinoxConfig.clearConfiguration(Constants.FRAMEWORK_BOOTDELEGATION); // override with a null value 1628 else 1629 equinoxConfig.setConfiguration(Constants.FRAMEWORK_BOOTDELEGATION, profileBootDelegation); // override with the profile value 1630 } else if (EquinoxConfiguration.PROP_OSGI_BOOTDELEGATION_NONE.equals(type)) 1631 equinoxConfig.clearConfiguration(Constants.FRAMEWORK_BOOTDELEGATION); // remove the bootdelegation property in case it was set 1632 // set the org.osgi.framework.executionenvironment property according to the java profile 1633 if (equinoxConfig.getConfiguration(Constants.FRAMEWORK_EXECUTIONENVIRONMENT) == null) { 1634 // get the ee from the java profile; if no ee is defined then try the java profile name 1635 String ee = profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, profileProps.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME)); 1636 if (ee != null) 1637 equinoxConfig.setConfiguration(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); 1638 } 1639 // set the org.osgi.framework.system.capabilities property according to the java profile 1640 if (equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES) == null) { 1641 String systemCapabilities = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES); 1642 if (systemCapabilities != null) 1643 equinoxConfig.setConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES, systemCapabilities); 1644 } 1645 } 1646 1647 private Properties findVMProfile(Generation systemGeneration) { 1648 Properties result = readConfiguredJavaProfile(systemGeneration); 1649 String vmProfile = null; 1650 try { 1651 if (result != null) { 1652 return result; 1653 } 1654 1655 if (Version.valueOf("9").compareTo(runtimeVersion) <= 0) { //$NON-NLS-1$ 1656 result = calculateVMProfile(runtimeVersion); 1657 if (result != null) { 1658 return result; 1659 } 1660 // could not calculate; fall back to reading profile files 1661 } 1662 1663 String embeddedProfileName = "-"; //$NON-NLS-1$ 1664 // If javaSE 1.8 then check for release file for profile name. 1665 if (runtimeVersion != null && Version.valueOf("1.8").compareTo(runtimeVersion) <= 0) { //$NON-NLS-1$ 1666 String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ 1667 if (javaHome != null) { 1668 File release = new File(javaHome, "release"); //$NON-NLS-1$ 1669 if (release.exists()) { 1670 Properties releaseProps = new Properties(); 1671 try (InputStream releaseStream = new FileInputStream(release)) { 1672 releaseProps.load(releaseStream); 1673 String releaseName = releaseProps.getProperty("JAVA_PROFILE"); //$NON-NLS-1$ 1674 if (releaseName != null) { 1675 // make sure to remove extra quotes 1676 releaseName = releaseName.replaceAll("^\\s*\"?|\"?\\s*$", ""); //$NON-NLS-1$ //$NON-NLS-2$ 1677 embeddedProfileName = "_" + releaseName + "-"; //$NON-NLS-1$ //$NON-NLS-2$ 1678 } 1679 } catch (IOException e) { 1680 // ignore 1681 } 1682 } 1683 } 1684 } 1685 1686 result = new Properties(); 1687 vmProfile = JAVASE + embeddedProfileName + javaSpecVersion; 1688 InputStream profileIn = null; 1689 if (vmProfile != null) { 1690 // look for a profile in the system bundle based on the vm profile 1691 String javaProfile = vmProfile + PROFILE_EXT; 1692 profileIn = findInSystemBundle(systemGeneration, javaProfile); 1693 if (profileIn == null) 1694 profileIn = getNextBestProfile(systemGeneration, JAVASE, runtimeVersion, embeddedProfileName); 1695 } 1696 if (profileIn == null) 1697 // the profile url is still null then use the min profile the framework can use 1698 profileIn = findInSystemBundle(systemGeneration, "JavaSE-1.7.profile"); //$NON-NLS-1$ 1699 if (profileIn != null) { 1700 try { 1701 result.load(new BufferedInputStream(profileIn)); 1702 } catch (IOException e) { 1703 // TODO consider logging ... 1704 } finally { 1705 try { 1706 profileIn.close(); 1707 } catch (IOException ee) { 1708 // do nothing 1709 } 1710 } 1711 } 1712 } finally { 1713 // set the profile name if it does not provide one 1714 if (result != null && result.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME) == null) { 1715 if (vmProfile != null) { 1716 result.put(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME, vmProfile.replace('_', '/')); 1717 } else { 1718 // last resort; default to the absolute minimum profile name for the framework 1719 result.put(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME, "JavaSE-1.7"); //$NON-NLS-1$ 1720 } 1721 } 1722 } 1723 return result; 1724 } 1725 1726 private Properties readConfiguredJavaProfile(Generation systemGeneration) { 1727 // check for the java profile property for a url 1728 String propJavaProfile = equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE); 1729 if (propJavaProfile != null) { 1730 InputStream profileIn = null; 1731 try { 1732 // we assume a URL 1733 profileIn = new URL(propJavaProfile).openStream(); 1734 } catch (IOException e) { 1735 // try using a relative path in the system bundle 1736 profileIn = findInSystemBundle(systemGeneration, propJavaProfile); 1737 } 1738 if (profileIn != null) { 1739 Properties result = new Properties(); 1740 try { 1741 result.load(new BufferedInputStream(profileIn)); 1742 } catch (IOException e) { 1743 // consider logging 1744 } finally { 1745 try { 1746 profileIn.close(); 1747 } catch (IOException e) { 1748 // nothing to do 1749 } 1750 } 1751 return result; 1752 } 1753 } 1754 return null; 1755 } 1756 1757 @SuppressWarnings("deprecation") 1758 private Properties calculateVMProfile(Version javaVersion) { 1759 String systemPackages = calculateVMPackages(); 1760 if (systemPackages == null) { 1761 return null; 1762 } 1763 String executionEnvs = calculateVMExecutionEnvs(javaVersion); 1764 String eeCapabilities = calculateEECapabilities(javaVersion); 1765 1766 Properties result = new Properties(); 1767 result.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); 1768 result.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, executionEnvs); 1769 result.put(Constants.FRAMEWORK_SYSTEMCAPABILITIES, eeCapabilities); 1770 return result; 1771 } 1772 1773 private String calculateVMExecutionEnvs(Version javaVersion) { 1774 StringBuilder result = new StringBuilder("OSGi/Minimum-1.0, OSGi/Minimum-1.1, OSGi/Minimum-1.2, JavaSE/compact1-1.8, JavaSE/compact2-1.8, JavaSE/compact3-1.8, JRE-1.1, J2SE-1.2, J2SE-1.3, J2SE-1.4, J2SE-1.5, JavaSE-1.6, JavaSE-1.7, JavaSE-1.8"); //$NON-NLS-1$ 1775 Version v = new Version(9, 0, 0); 1776 while (v.compareTo(javaVersion) <= 0) { 1777 result.append(',').append(' ').append(JAVASE).append('-').append(v.getMajor()); 1778 if (v.getMinor() > 0) { 1779 result.append('.').append(v.getMinor()); 1780 } 1781 if (v.getMajor() == javaVersion.getMajor()) { 1782 v = new Version(v.getMajor(), v.getMinor() + 1, 0); 1783 } else { 1784 v = new Version(v.getMajor() + 1, 0, 0); 1785 } 1786 } 1787 return result.toString(); 1788 } 1789 1790 private String calculateEECapabilities(Version javaVersion) { 1791 Version v = new Version(9, 0, 0); 1792 StringBuilder versionsBulder = new StringBuilder(); 1793 while (v.compareTo(javaVersion) <= 0) { 1794 versionsBulder.append(',').append(' ').append(v.getMajor()).append('.').append(v.getMinor()); 1795 if (v.getMajor() == javaVersion.getMajor()) { 1796 v = new Version(v.getMajor(), v.getMinor() + 1, 0); 1797 } else { 1798 v = new Version(v.getMajor() + 1, 0, 0); 1799 } 1800 } 1801 String versionsList = versionsBulder.toString(); 1802 1803 StringBuilder result = new StringBuilder("osgi.ee; osgi.ee=\"OSGi/Minimum\"; version:List<Version>=\"1.0, 1.1, 1.2\", osgi.ee; osgi.ee=\"JRE\"; version:List<Version>=\"1.0, 1.1\", osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8"); //$NON-NLS-1$ 1804 result.append(versionsList).append("\""); //$NON-NLS-1$ 1805 result.append(",osgi.ee; osgi.ee=\"JavaSE/compact1\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ 1806 result.append(versionsList).append("\""); //$NON-NLS-1$ 1807 result.append(",osgi.ee; osgi.ee=\"JavaSE/compact2\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ 1808 result.append(versionsList).append("\""); //$NON-NLS-1$ 1809 result.append(",osgi.ee; osgi.ee=\"JavaSE/compact3\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ 1810 result.append(versionsList).append("\""); //$NON-NLS-1$ 1811 1812 return result.toString(); 1813 } 1814 1815 @SuppressWarnings("unchecked") 1816 private String calculateVMPackages() { 1817 try { 1818 List<String> packages = new ArrayList<>(); 1819 Class<?> moduleLayerClass = Class.forName("java.lang.ModuleLayer"); //$NON-NLS-1$ 1820 Method boot = moduleLayerClass.getMethod("boot"); //$NON-NLS-1$ 1821 Method modules = moduleLayerClass.getMethod("modules"); //$NON-NLS-1$ 1822 Class<?> moduleClass = Class.forName("java.lang.Module"); //$NON-NLS-1$ 1823 Method getDescriptor = moduleClass.getMethod("getDescriptor"); //$NON-NLS-1$ 1824 Class<?> moduleDescriptorClass = Class.forName("java.lang.module.ModuleDescriptor"); //$NON-NLS-1$ 1825 Method exports = moduleDescriptorClass.getMethod("exports"); //$NON-NLS-1$ 1826 Method isAutomatic = moduleDescriptorClass.getMethod("isAutomatic"); //$NON-NLS-1$ 1827 Method packagesMethod = moduleDescriptorClass.getMethod("packages"); //$NON-NLS-1$ 1828 Class<?> exportsClass = Class.forName("java.lang.module.ModuleDescriptor$Exports"); //$NON-NLS-1$ 1829 Method isQualified = exportsClass.getMethod("isQualified"); //$NON-NLS-1$ 1830 Method source = exportsClass.getMethod("source"); //$NON-NLS-1$ 1831 1832 Object bootLayer = boot.invoke(null); 1833 Set<?> bootModules = (Set<?>) modules.invoke(bootLayer); 1834 for (Object m : bootModules) { 1835 Object descriptor = getDescriptor.invoke(m); 1836 if ((Boolean) isAutomatic.invoke(descriptor)) { 1837 /* 1838 * Automatic modules are supposed to export all their packages. 1839 * However, java.lang.module.ModuleDescriptor::exports returns an empty set for them. 1840 * Add all their packages (as returned by java.lang.module.ModuleDescriptor::packages) 1841 * to the list of VM supplied packages. 1842 */ 1843 packages.addAll((Set<String>) packagesMethod.invoke(descriptor)); 1844 } else { 1845 for (Object export : (Set<?>) exports.invoke(descriptor)) { 1846 String pkg = (String) source.invoke(export); 1847 if (!((Boolean) isQualified.invoke(export))) { 1848 packages.add(pkg); 1849 } 1850 } 1851 } 1852 } 1853 Collections.sort(packages); 1854 StringBuilder result = new StringBuilder(); 1855 for (String pkg : packages) { 1856 if (result.length() != 0) { 1857 result.append(',').append(' '); 1858 } 1859 result.append(pkg); 1860 } 1861 return result.toString(); 1862 } catch (Exception e) { 1863 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error determining system packages.", e); //$NON-NLS-1$ 1864 return null; 1865 } 1866 } 1867 1868 private InputStream getNextBestProfile(Generation systemGeneration, String javaEdition, Version javaVersion, String embeddedProfileName) { 1869 if (javaVersion == null || javaEdition != JAVASE) 1870 return null; // we cannot automatically choose the next best profile unless this is a JavaSE vm 1871 InputStream bestProfile = findNextBestProfile(systemGeneration, javaEdition, javaVersion, embeddedProfileName); 1872 if (bestProfile == null && !"-".equals(embeddedProfileName)) { //$NON-NLS-1$ 1873 // Just use the base javaEdition name without the profile name as backup 1874 return getNextBestProfile(systemGeneration, javaEdition, javaVersion, "-"); //$NON-NLS-1$ 1875 } 1876 return bestProfile; 1877 } 1878 1879 private InputStream findNextBestProfile(Generation systemGeneration, String javaEdition, Version javaVersion, String embeddedProfileName) { 1880 InputStream result = null; 1881 int major = javaVersion.getMajor(); 1882 int minor = javaVersion.getMinor(); 1883 do { 1884 // If minor is zero then it is not included in the name 1885 String profileResourceName = javaEdition + embeddedProfileName + major + ((minor > 0) ? "." + minor : "") + PROFILE_EXT; //$NON-NLS-1$ //$NON-NLS-2$ 1886 result = findInSystemBundle(systemGeneration, profileResourceName); 1887 if (minor > 0) { 1888 minor -= 1; 1889 } else if (major > 9) { 1890 major -= 1; 1891 } else if (major <= 9 && major > 1) { 1892 minor = 8; 1893 major = 1; 1894 } else { 1895 // we have reached the end of our search; return the existing result; 1896 return result; 1897 } 1898 } while (result == null && minor >= 0); 1899 return result; 1900 } 1901 1902 private InputStream findInSystemBundle(Generation systemGeneration, String entry) { 1903 BundleFile systemContent = systemGeneration.getBundleFile(); 1904 BundleEntry systemEntry = systemContent != null ? systemContent.getEntry(entry) : null; 1905 InputStream result = null; 1906 if (systemEntry != null) { 1907 try { 1908 result = systemEntry.getInputStream(); 1909 } catch (IOException e) { 1910 // Do nothing 1911 } 1912 } 1913 if (result == null) { 1914 // Check the ClassLoader in case we're launched off the Java boot classpath 1915 ClassLoader loader = getClass().getClassLoader(); 1916 result = loader == null ? ClassLoader.getSystemResourceAsStream(entry) : loader.getResourceAsStream(entry); 1917 } 1918 return result; 1919 } 1920 1921 public static Enumeration<URL> findEntries(List<Generation> generations, String path, String filePattern, int options) { 1922 List<BundleFile> bundleFiles = new ArrayList<>(generations.size()); 1923 for (Generation generation : generations) 1924 bundleFiles.add(generation.getBundleFile()); 1925 // search all the bundle files 1926 List<String> pathList = listEntryPaths(bundleFiles, path, filePattern, options); 1927 // return null if no entries found 1928 if (pathList.size() == 0) 1929 return null; 1930 // create an enumeration to enumerate the pathList 1931 final String[] pathArray = pathList.toArray(new String[pathList.size()]); 1932 final Generation[] generationArray = generations.toArray(new Generation[generations.size()]); 1933 return new Enumeration<URL>() { 1934 private int curPathIndex = 0; 1935 private int curDataIndex = 0; 1936 private URL nextElement = null; 1937 1938 @Override 1939 public boolean hasMoreElements() { 1940 if (nextElement != null) 1941 return true; 1942 getNextElement(); 1943 return nextElement != null; 1944 } 1945 1946 @Override 1947 public URL nextElement() { 1948 if (!hasMoreElements()) 1949 throw new NoSuchElementException(); 1950 URL result = nextElement; 1951 // force the next element search 1952 getNextElement(); 1953 return result; 1954 } 1955 1956 private void getNextElement() { 1957 nextElement = null; 1958 if (curPathIndex >= pathArray.length) 1959 // reached the end of the pathArray; no more elements 1960 return; 1961 while (nextElement == null && curPathIndex < pathArray.length) { 1962 String curPath = pathArray[curPathIndex]; 1963 // search the generation until we have searched them all 1964 while (nextElement == null && curDataIndex < generationArray.length) 1965 nextElement = generationArray[curDataIndex++].getEntry(curPath); 1966 // we have searched all datas then advance to the next path 1967 if (curDataIndex >= generationArray.length) { 1968 curPathIndex++; 1969 curDataIndex = 0; 1970 } 1971 } 1972 } 1973 }; 1974 } 1975 1976 /** 1977 * Returns the names of resources available from a list of bundle files. 1978 * No duplicate resource names are returned, each name is unique. 1979 * @param bundleFiles the list of bundle files to search in 1980 * @param path The path name in which to look. 1981 * @param filePattern The file name pattern for selecting resource names in 1982 * the specified path. 1983 * @param options The options for listing resource names. 1984 * @return a list of resource names. If no resources are found then 1985 * the empty list is returned. 1986 * @see BundleWiring#listResources(String, String, int) 1987 */ 1988 public static List<String> listEntryPaths(List<BundleFile> bundleFiles, String path, String filePattern, int options) { 1989 // Use LinkedHashSet for optimized performance of contains() plus 1990 // ordering guarantees. 1991 LinkedHashSet<String> pathList = new LinkedHashSet<>(); 1992 Filter patternFilter = null; 1993 Hashtable<String, String> patternProps = null; 1994 if (filePattern != null) { 1995 // Optimization: If the file pattern does not include a wildcard or escape char then it must represent a single file. 1996 // Avoid pattern matching and use BundleFile.getEntry() if recursion was not requested. 1997 if ((options & BundleWiring.FINDENTRIES_RECURSE) == 0 && filePattern.indexOf('*') == -1 && filePattern.indexOf('\\') == -1) { 1998 if (path.length() == 0) 1999 path = filePattern; 2000 else 2001 path += path.charAt(path.length() - 1) == '/' ? filePattern : '/' + filePattern; 2002 for (BundleFile bundleFile : bundleFiles) { 2003 if (bundleFile.getEntry(path) != null && !pathList.contains(path)) 2004 pathList.add(path); 2005 } 2006 return new ArrayList<>(pathList); 2007 } 2008 // For when the file pattern includes a wildcard. 2009 try { 2010 // create a file pattern filter with 'filename' as the key 2011 patternFilter = FilterImpl.newInstance("(filename=" + sanitizeFilterInput(filePattern) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ 2012 // create a single hashtable to be shared during the recursive search 2013 patternProps = new Hashtable<>(2); 2014 } catch (InvalidSyntaxException e) { 2015 // TODO something unexpected happened; log error and return nothing 2016 // Bundle b = context == null ? null : context.getBundle(); 2017 // eventPublisher.publishFrameworkEvent(FrameworkEvent.ERROR, b, e); 2018 return new ArrayList<>(pathList); 2019 } 2020 } 2021 // find the entry paths for the datas 2022 for (BundleFile bundleFile : bundleFiles) { 2023 listEntryPaths(bundleFile, path, patternFilter, patternProps, options, pathList); 2024 } 2025 return new ArrayList<>(pathList); 2026 } 2027 2028 public static String sanitizeFilterInput(String filePattern) throws InvalidSyntaxException { 2029 StringBuilder buffer = null; 2030 boolean foundEscape = false; 2031 for (int i = 0; i < filePattern.length(); i++) { 2032 char c = filePattern.charAt(i); 2033 switch (c) { 2034 case '\\' : 2035 // we either used the escape found or found a new escape. 2036 foundEscape = foundEscape ? false : true; 2037 if (buffer != null) 2038 buffer.append(c); 2039 break; 2040 case '(' : 2041 case ')' : 2042 if (!foundEscape) { 2043 if (buffer == null) { 2044 buffer = new StringBuilder(filePattern.length() + 16); 2045 buffer.append(filePattern.substring(0, i)); 2046 } 2047 // must escape with '\' 2048 buffer.append('\\'); 2049 } else { 2050 foundEscape = false; // used the escape found 2051 } 2052 if (buffer != null) 2053 buffer.append(c); 2054 break; 2055 default : 2056 // if we found an escape it has been used 2057 foundEscape = false; 2058 if (buffer != null) 2059 buffer.append(c); 2060 break; 2061 } 2062 } 2063 if (foundEscape) 2064 throw new InvalidSyntaxException("Trailing escape characters must be escaped.", filePattern); //$NON-NLS-1$ 2065 return buffer == null ? filePattern : buffer.toString(); 2066 } 2067 2068 // Use LinkedHashSet for optimized performance of contains() plus ordering 2069 // guarantees. 2070 private static LinkedHashSet<String> listEntryPaths(BundleFile bundleFile, String path, Filter patternFilter, Hashtable<String, String> patternProps, int options, LinkedHashSet<String> pathList) { 2071 if (pathList == null) 2072 pathList = new LinkedHashSet<>(); 2073 Enumeration<String> entryPaths; 2074 if ((options & BundleWiring.FINDENTRIES_RECURSE) != 0) 2075 entryPaths = bundleFile.getEntryPaths(path, true); 2076 else 2077 entryPaths = bundleFile.getEntryPaths(path); 2078 if (entryPaths == null) 2079 return pathList; 2080 while (entryPaths.hasMoreElements()) { 2081 String entry = entryPaths.nextElement(); 2082 int lastSlash = entry.lastIndexOf('/'); 2083 if (patternProps != null) { 2084 int secondToLastSlash = entry.lastIndexOf('/', lastSlash - 1); 2085 int fileStart; 2086 int fileEnd = entry.length(); 2087 if (lastSlash < 0) 2088 fileStart = 0; 2089 else if (lastSlash != entry.length() - 1) 2090 fileStart = lastSlash + 1; 2091 else { 2092 fileEnd = lastSlash; // leave the lastSlash out 2093 if (secondToLastSlash < 0) 2094 fileStart = 0; 2095 else 2096 fileStart = secondToLastSlash + 1; 2097 } 2098 String fileName = entry.substring(fileStart, fileEnd); 2099 // set the filename to the current entry 2100 patternProps.put("filename", fileName); //$NON-NLS-1$ 2101 } 2102 // prevent duplicates and match on the patternFilter 2103 if (!pathList.contains(entry) && (patternFilter == null || patternFilter.matchCase(patternProps))) 2104 pathList.add(entry); 2105 } 2106 return pathList; 2107 } 2108 2109 public String copyToTempLibrary(Generation generation, String absolutePath) { 2110 File libTempDir = new File(childRoot, LIB_TEMP); 2111 // we assume the absolutePath is a File path 2112 File realLib = new File(absolutePath); 2113 String libName = realLib.getName(); 2114 // find a temp dir for the bundle data and the library; 2115 File bundleTempDir = null; 2116 File libTempFile = null; 2117 // We need a somewhat predictable temp dir for the libraries of a given bundle; 2118 // This is not strictly necessary but it does help scenarios where one native library loads another native library without using java. 2119 // On some OSes this causes issues because the second library is cannot be found. 2120 // This has been worked around by the bundles loading the libraries in a particular order (and setting some LIB_PATH env). 2121 // The one catch is that the libraries need to be in the same directory and they must use their original lib names. 2122 // 2123 // This bit of code attempts to do that by using the bundle ID as an ID for the temp dir along with an incrementing ID 2124 // in cases where the temp dir may already exist. 2125 Long bundleID = new Long(generation.getBundleInfo().getBundleId()); 2126 for (int i = 0; i < Integer.MAX_VALUE; i++) { 2127 bundleTempDir = new File(libTempDir, bundleID.toString() + "_" + Integer.valueOf(i).toString()); //$NON-NLS-1$ 2128 libTempFile = new File(bundleTempDir, libName); 2129 if (bundleTempDir.exists()) { 2130 if (libTempFile.exists()) 2131 continue; // to to next temp file 2132 break; 2133 } 2134 break; 2135 } 2136 if (!bundleTempDir.isDirectory()) { 2137 bundleTempDir.mkdirs(); 2138 bundleTempDir.deleteOnExit(); 2139 // This is just a safeguard incase the VM is terminated unexpectantly, it also looks like deleteOnExit cannot really work because 2140 // the VM likely will still have a lock on the lib file at the time of VM exit. 2141 File deleteFlag = new File(libTempDir, DELETE_FLAG); 2142 if (!deleteFlag.exists()) { 2143 // need to create a delete flag to force removal the temp libraries 2144 try { 2145 FileOutputStream out = new FileOutputStream(deleteFlag); 2146 out.close(); 2147 } catch (IOException e) { 2148 // do nothing; that would mean we did not make the temp dir successfully 2149 } 2150 } 2151 } 2152 // copy the library file 2153 try { 2154 InputStream in = new FileInputStream(realLib); 2155 StorageUtil.readFile(in, libTempFile); 2156 // set permissions if needed 2157 setPermissions(libTempFile); 2158 libTempFile.deleteOnExit(); // this probably will not work because the VM will probably have the lib locked at exit 2159 // return the temporary path 2160 return libTempFile.getAbsolutePath(); 2161 } catch (IOException e) { 2162 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, e.getMessage(), e); 2163 return null; 2164 } 2165 } 2166 2167 public SecurityAdmin getSecurityAdmin() { 2168 return securityAdmin; 2169 } 2170 2171 protected StorageManager getChildStorageManager() throws IOException { 2172 String locking = getConfiguration().getConfiguration(LocationHelper.PROP_OSGI_LOCKING, LocationHelper.LOCKING_NIO); 2173 StorageManager sManager = new StorageManager(childRoot, isReadOnly() ? LocationHelper.LOCKING_NONE : locking, isReadOnly()); 2174 try { 2175 sManager.open(!isReadOnly()); 2176 } catch (IOException ex) { 2177 if (getConfiguration().getDebug().DEBUG_STORAGE) { 2178 Debug.println("Error reading framework.info: " + ex.getMessage()); //$NON-NLS-1$ 2179 Debug.printStackTrace(ex); 2180 } 2181 String message = NLS.bind(Msg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, ex.getMessage()); 2182 equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, message, ex); 2183 getConfiguration().setProperty(EclipseStarter.PROP_EXITCODE, "15"); //$NON-NLS-1$ 2184 String errorDialog = "<title>" + Msg.ADAPTOR_STORAGE_INIT_FAILED_TITLE + "</title>" + NLS.bind(Msg.ADAPTOR_STORAGE_INIT_FAILED_MSG, childRoot) + "\n" + ex.getMessage(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 2185 getConfiguration().setProperty(EclipseStarter.PROP_EXITDATA, errorDialog); 2186 throw ex; 2187 } 2188 return sManager; 2189 } 2190 2191 private InputStream getInfoInputStream() throws IOException { 2192 StorageManager storageManager = getChildStorageManager(); 2193 InputStream storageStream = null; 2194 try { 2195 storageStream = storageManager.getInputStream(FRAMEWORK_INFO); 2196 } catch (IOException ex) { 2197 if (getConfiguration().getDebug().DEBUG_STORAGE) { 2198 Debug.println("Error reading framework.info: " + ex.getMessage()); //$NON-NLS-1$ 2199 Debug.printStackTrace(ex); 2200 } 2201 } finally { 2202 storageManager.close(); 2203 } 2204 if (storageStream == null && parentRoot != null) { 2205 StorageManager parentStorageManager = null; 2206 try { 2207 parentStorageManager = new StorageManager(parentRoot, LocationHelper.LOCKING_NONE, true); 2208 parentStorageManager.open(false); 2209 storageStream = parentStorageManager.getInputStream(FRAMEWORK_INFO); 2210 } catch (IOException e1) { 2211 // That's ok we will regenerate the framework.info 2212 } finally { 2213 if (parentStorageManager != null) { 2214 parentStorageManager.close(); 2215 } 2216 } 2217 } 2218 return storageStream; 2219 } 2220 } 2221