/******************************************************************************* * Copyright (c) 2012, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.osgi.storage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.adaptor.EclipseStarter; import org.eclipse.osgi.container.Module; import org.eclipse.osgi.container.ModuleCapability; import org.eclipse.osgi.container.ModuleContainer; import org.eclipse.osgi.container.ModuleContainerAdaptor; import org.eclipse.osgi.container.ModuleDatabase; import org.eclipse.osgi.container.ModuleRevision; import org.eclipse.osgi.container.ModuleRevisionBuilder; import org.eclipse.osgi.container.ModuleRevisionBuilder.GenericInfo; import org.eclipse.osgi.container.ModuleWire; import org.eclipse.osgi.container.ModuleWiring; import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory; import org.eclipse.osgi.container.namespaces.EclipsePlatformNamespace; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.framework.util.FilePath; import org.eclipse.osgi.framework.util.ObjectPool; import org.eclipse.osgi.framework.util.SecureAction; import org.eclipse.osgi.internal.debug.Debug; import org.eclipse.osgi.internal.framework.EquinoxConfiguration; import org.eclipse.osgi.internal.framework.EquinoxContainer; import org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor; import org.eclipse.osgi.internal.framework.FilterImpl; import org.eclipse.osgi.internal.hookregistry.BundleFileWrapperFactoryHook; import org.eclipse.osgi.internal.hookregistry.StorageHookFactory; import org.eclipse.osgi.internal.hookregistry.StorageHookFactory.StorageHook; import org.eclipse.osgi.internal.location.EquinoxLocations; import org.eclipse.osgi.internal.location.LocationHelper; import org.eclipse.osgi.internal.log.EquinoxLogServices; import org.eclipse.osgi.internal.messages.Msg; import org.eclipse.osgi.internal.permadmin.SecurityAdmin; import org.eclipse.osgi.internal.url.URLStreamHandlerFactoryImpl; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.storage.BundleInfo.Generation; import org.eclipse.osgi.storage.bundlefile.BundleEntry; import org.eclipse.osgi.storage.bundlefile.BundleFile; import org.eclipse.osgi.storage.bundlefile.BundleFileWrapper; import org.eclipse.osgi.storage.bundlefile.BundleFileWrapperChain; import org.eclipse.osgi.storage.bundlefile.DirBundleFile; import org.eclipse.osgi.storage.bundlefile.MRUBundleFileList; import org.eclipse.osgi.storage.bundlefile.NestedDirBundleFile; import org.eclipse.osgi.storage.bundlefile.ZipBundleFile; import org.eclipse.osgi.storage.url.reference.Handler; import org.eclipse.osgi.storage.url.reference.ReferenceInputStream; import org.eclipse.osgi.storagemanager.ManagedOutputStream; import org.eclipse.osgi.storagemanager.StorageManager; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.Version; import org.osgi.framework.namespace.HostNamespace; import org.osgi.framework.namespace.NativeNamespace; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.BundleWiring; import org.osgi.resource.Namespace; import org.osgi.resource.Requirement; public class Storage { public static final int VERSION = 4; private static final int MR_JAR_VERSION = 4; private static final int LOWEST_VERSION_SUPPORTED = 3; public static final String BUNDLE_DATA_DIR = "data"; //$NON-NLS-1$ public static final String BUNDLE_FILE_NAME = "bundleFile"; //$NON-NLS-1$ public static final String FRAMEWORK_INFO = "framework.info"; //$NON-NLS-1$ public static final String ECLIPSE_SYSTEMBUNDLE = "Eclipse-SystemBundle"; //$NON-NLS-1$ public static final String DELETE_FLAG = ".delete"; //$NON-NLS-1$ public static final String LIB_TEMP = "libtemp"; //$NON-NLS-1$ private static final String JAVASE = "JavaSE"; //$NON-NLS-1$ private static final String PROFILE_EXT = ".profile"; //$NON-NLS-1$ private static final String NUL = new String(new byte[] {0}); private static final String INITIAL_LOCATION = "initial@"; //$NON-NLS-1$ static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction()); private final EquinoxContainer equinoxContainer; private final String installPath; private final Location osgiLocation; private final File childRoot; private final File parentRoot; private final PermissionData permissionData; private final SecurityAdmin securityAdmin; private final EquinoxContainerAdaptor adaptor; private final ModuleDatabase moduleDatabase; private final ModuleContainer moduleContainer; private final Object saveMonitor = new Object(); private long lastSavedTimestamp = -1; private final MRUBundleFileList mruList; private final FrameworkExtensionInstaller extensionInstaller; private final List cachedHeaderKeys = Arrays.asList(Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_ACTIVATIONPOLICY, "Service-Component"); //$NON-NLS-1$ private final boolean allowRestrictedProvides; private final AtomicBoolean refreshMRBundles = new AtomicBoolean(false); private final Version runtimeVersion; private final String javaSpecVersion; public static Storage createStorage(EquinoxContainer container) throws IOException, BundleException { Storage storage = new Storage(container); // Do some operations that need to happen on the fully constructed Storage before returning it storage.checkSystemBundle(); storage.refreshStaleBundles(); storage.installExtensions(); // TODO hack to make sure all bundles are in UNINSTALLED state before system bundle init is called storage.getModuleContainer().setInitialModuleStates(); return storage; } private Storage(EquinoxContainer container) throws IOException { // default to Java 7 since that is our min Version javaVersion = Version.valueOf("1.7"); //$NON-NLS-1$ // set the profile and EE based off of the java.specification.version String javaSpecVersionProp = System.getProperty(EquinoxConfiguration.PROP_JVM_SPEC_VERSION); StringTokenizer st = new StringTokenizer(javaSpecVersionProp, " _-"); //$NON-NLS-1$ javaSpecVersionProp = st.nextToken(); try { String[] vComps = javaSpecVersionProp.split("\\."); //$NON-NLS-1$ // only pay attention to the first three components of the version int major = vComps.length > 0 ? Integer.parseInt(vComps[0]) : 0; int minor = vComps.length > 1 ? Integer.parseInt(vComps[1]) : 0; int micro = vComps.length > 2 ? Integer.parseInt(vComps[2]) : 0; javaVersion = new Version(major, minor, micro); } catch (IllegalArgumentException e) { // do nothing } runtimeVersion = javaVersion; javaSpecVersion = javaSpecVersionProp; mruList = new MRUBundleFileList(getBundleFileLimit(container.getConfiguration())); equinoxContainer = container; extensionInstaller = new FrameworkExtensionInstaller(container.getConfiguration()); allowRestrictedProvides = Boolean.parseBoolean(container.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_ALLOW_RESTRICTED_PROVIDES)); // we need to set the install path as soon as possible so we can determine // the absolute location of install relative URLs Location installLoc = container.getLocations().getInstallLocation(); URL installURL = installLoc.getURL(); // assume install URL is file: based installPath = installURL.getPath(); Location configLocation = container.getLocations().getConfigurationLocation(); Location parentConfigLocation = configLocation.getParentLocation(); Location osgiParentLocation = null; if (parentConfigLocation != null) { osgiParentLocation = parentConfigLocation.createLocation(null, parentConfigLocation.getDataArea(EquinoxContainer.NAME), true); } this.osgiLocation = configLocation.createLocation(osgiParentLocation, configLocation.getDataArea(EquinoxContainer.NAME), configLocation.isReadOnly()); this.childRoot = new File(osgiLocation.getURL().getPath()); if (Boolean.valueOf(container.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_CLEAN)).booleanValue()) { cleanOSGiStorage(osgiLocation, childRoot); } if (!this.osgiLocation.isReadOnly()) { this.childRoot.mkdirs(); } Location parent = this.osgiLocation.getParentLocation(); parentRoot = parent == null ? null : new File(parent.getURL().getPath()); if (container.getConfiguration().getConfiguration(Constants.FRAMEWORK_STORAGE) == null) { // Set the derived value if not already set as part of configuration. // Note this is the parent directory of where the framework stores data (org.eclipse.osgi/) container.getConfiguration().setConfiguration(Constants.FRAMEWORK_STORAGE, childRoot.getParentFile().getAbsolutePath()); } InputStream info = getInfoInputStream(); DataInputStream data = info == null ? null : new DataInputStream(new BufferedInputStream(info)); try { Map generations; try { generations = loadGenerations(data); } catch (IllegalArgumentException e) { 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$ generations = new HashMap<>(0); data = null; cleanOSGiStorage(osgiLocation, childRoot); } this.permissionData = loadPermissionData(data); this.securityAdmin = new SecurityAdmin(null, this.permissionData); this.adaptor = new EquinoxContainerAdaptor(equinoxContainer, this, generations); this.moduleDatabase = new ModuleDatabase(this.adaptor); this.moduleContainer = new ModuleContainer(this.adaptor, this.moduleDatabase); if (data != null) { try { moduleDatabase.load(data); lastSavedTimestamp = moduleDatabase.getTimestamp(); } catch (IllegalArgumentException e) { equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, "Incompatible version. Starting with empty framework.", e); //$NON-NLS-1$ // Clean up the cache. // No need to clean up the database. Nothing got loaded. cleanOSGiStorage(osgiLocation, childRoot); // should free up the generations map generations.clear(); } } } finally { if (data != null) { try { data.close(); } catch (IOException e) { // just move on } } } } public Version getRuntimeVersion() { return runtimeVersion; } private int getBundleFileLimit(EquinoxConfiguration configuration) { int propValue = 100; // enable to 100 open files by default try { String prop = configuration.getConfiguration(EquinoxConfiguration.PROP_FILE_LIMIT); if (prop != null) propValue = Integer.parseInt(prop); } catch (NumberFormatException e) { // use default of 100 } return propValue; } private void installExtensions() { Module systemModule = moduleContainer.getModule(0); ModuleRevision systemRevision = systemModule == null ? null : systemModule.getCurrentRevision(); ModuleWiring systemWiring = systemRevision == null ? null : systemRevision.getWiring(); if (systemWiring == null) { return; } Collection fragments = new ArrayList<>(); for (ModuleWire hostWire : systemWiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE)) { fragments.add(hostWire.getRequirer()); } try { getExtensionInstaller().addExtensionContent(fragments, null); } catch (BundleException e) { getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, e.getMessage(), e); } } private static PermissionData loadPermissionData(DataInputStream in) throws IOException { PermissionData permData = new PermissionData(); if (in != null) { permData.readPermissionData(in); } return permData; } private void refreshStaleBundles() throws BundleException { Collection needsRefresh = new ArrayList<>(0); // First uninstall any modules that had their content changed or deleted for (Module module : moduleContainer.getModules()) { if (module.getId() == Constants.SYSTEM_BUNDLE_ID) continue; ModuleRevision revision = module.getCurrentRevision(); Generation generation = (Generation) revision.getRevisionInfo(); if (needsDiscarding(generation)) { needsRefresh.add(module); moduleContainer.uninstall(module); generation.delete(); } } // Next check if we need to refresh Multi-Release Jar bundles // because the runtime version changed. if (refreshMRBundles.get()) { needsRefresh.addAll(refreshMRJarBundles()); } // refresh the modules that got deleted or are Multi-Release bundles if (!needsRefresh.isEmpty()) { moduleContainer.refresh(needsRefresh); } } private boolean needsDiscarding(Generation generation) { for (StorageHook hook : generation.getStorageHooks()) { try { hook.validate(); } catch (IllegalStateException e) { // TODO Logging? return true; } } File content = generation.getContent(); if (getConfiguration().inCheckConfigurationMode()) { if (generation.isDirectory()) { content = new File(content, "META-INF/MANIFEST.MF"); //$NON-NLS-1$ } return generation.getLastModified() != secureAction.lastModified(content); } if (!content.exists()) { // the content got deleted since last time! return true; } return false; } private void checkSystemBundle() { Module systemModule = moduleContainer.getModule(0); Generation newGeneration = null; try { if (systemModule == null) { BundleInfo info = new BundleInfo(this, 0, Constants.SYSTEM_BUNDLE_LOCATION, 0); newGeneration = info.createGeneration(); File contentFile = getSystemContent(); newGeneration.setContent(contentFile, false); ModuleRevisionBuilder builder = getBuilder(newGeneration); systemModule = moduleContainer.install(null, Constants.SYSTEM_BUNDLE_LOCATION, builder, newGeneration); moduleContainer.resolve(Arrays.asList(systemModule), false); } else { ModuleRevision currentRevision = systemModule.getCurrentRevision(); Generation currentGeneration = currentRevision == null ? null : (Generation) currentRevision.getRevisionInfo(); if (currentGeneration == null) { throw new IllegalStateException("No current revision for system bundle."); //$NON-NLS-1$ } try { ModuleRevisionBuilder newBuilder = getBuilder(currentGeneration); if (needUpdate(currentRevision, newBuilder)) { newGeneration = currentGeneration.getBundleInfo().createGeneration(); File contentFile = getSystemContent(); newGeneration.setContent(contentFile, false); moduleContainer.update(systemModule, newBuilder, newGeneration); moduleContainer.refresh(Collections.singleton(systemModule)); } else { if (currentRevision.getWiring() == null) { // must resolve before continuing to ensure extensions get attached moduleContainer.resolve(Collections.singleton(systemModule), true); } } } catch (BundleException e) { throw new IllegalStateException("Could not create a builder for the system bundle.", e); //$NON-NLS-1$ } } ModuleRevision currentRevision = systemModule.getCurrentRevision(); List nativeEnvironments = currentRevision.getModuleCapabilities(NativeNamespace.NATIVE_NAMESPACE); Map configMap = equinoxContainer.getConfiguration().getInitialConfig(); for (ModuleCapability nativeEnvironment : nativeEnvironments) { nativeEnvironment.setTransientAttrs(configMap); } Requirement osgiPackageReq = ModuleContainer.createRequirement(PackageNamespace.PACKAGE_NAMESPACE, Collections.singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, "(" + PackageNamespace.PACKAGE_NAMESPACE + "=org.osgi.framework)"), Collections. emptyMap()); //$NON-NLS-1$ //$NON-NLS-2$ Collection osgiPackages = moduleContainer.getFrameworkWiring().findProviders(osgiPackageReq); for (BundleCapability packageCapability : osgiPackages) { if (packageCapability.getRevision().getBundle().getBundleId() == 0) { Version v = (Version) packageCapability.getAttributes().get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE); if (v != null) { this.equinoxContainer.getConfiguration().setConfiguration(Constants.FRAMEWORK_VERSION, v.toString()); break; } } } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException("Error occurred while checking the system module.", e); //$NON-NLS-1$ } finally { if (newGeneration != null) { newGeneration.getBundleInfo().unlockGeneration(newGeneration); } } } private Collection refreshMRJarBundles() throws BundleException { Collection mrJarBundles = new ArrayList<>(); for (Module m : moduleContainer.getModules()) { Generation generation = (Generation) m.getCurrentRevision().getRevisionInfo(); // Note that we check the raw headers here incase we are working off an old version of the persistent storage if (Boolean.parseBoolean(generation.getRawHeaders().get(BundleInfo.MULTI_RELEASE_HEADER))) { refresh(m); mrJarBundles.add(m); } } return mrJarBundles; } public void close() { try { save(); } catch (IOException e) { getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error saving on shutdown", e); //$NON-NLS-1$ } // close all the generations List modules = moduleContainer.getModules(); for (Module module : modules) { for (ModuleRevision revision : module.getRevisions().getModuleRevisions()) { Generation generation = (Generation) revision.getRevisionInfo(); if (generation != null) { generation.close(); } } } for (ModuleRevision removalPending : moduleContainer.getRemovalPending()) { Generation generation = (Generation) removalPending.getRevisionInfo(); if (generation != null) { generation.close(); } } mruList.shutdown(); adaptor.shutdownResolverExecutor(); } private boolean needUpdate(ModuleRevision currentRevision, ModuleRevisionBuilder newBuilder) { if (!currentRevision.getVersion().equals(newBuilder.getVersion())) { return true; } // Always do the advanced check for bug 432485 to make sure we have a consistent system bundle List currentCapabilities = currentRevision.getModuleCapabilities(null); List newCapabilities = newBuilder.getCapabilities(); if (currentCapabilities.size() != newCapabilities.size()) { return true; } int size = currentCapabilities.size(); for (int i = 0; i < size; i++) { if (!equivilant(currentCapabilities.get(i), newCapabilities.get(i))) { return true; } } return false; } private boolean equivilant(ModuleCapability moduleCapability, GenericInfo genericInfo) { if (!moduleCapability.getNamespace().equals(genericInfo.getNamespace())) { return false; } if (!moduleCapability.getAttributes().equals(genericInfo.getAttributes())) { return false; } if (!moduleCapability.getDirectives().equals(genericInfo.getDirectives())) { return false; } return true; } private void cleanOSGiStorage(Location location, File root) { if (location.isReadOnly() || !StorageUtil.rm(root, getConfiguration().getDebug().DEBUG_STORAGE)) { 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$ } if (!location.isReadOnly()) { // make sure to recreate to root folder root.mkdirs(); } } public ModuleDatabase getModuleDatabase() { return moduleDatabase; } public ModuleContainerAdaptor getAdaptor() { return adaptor; } public ModuleContainer getModuleContainer() { return moduleContainer; } public EquinoxConfiguration getConfiguration() { return equinoxContainer.getConfiguration(); } public EquinoxLogServices getLogServices() { return equinoxContainer.getLogServices(); } public FrameworkExtensionInstaller getExtensionInstaller() { return extensionInstaller; } public boolean isReadOnly() { return osgiLocation.isReadOnly(); } public URLConnection getContentConnection(Module module, String bundleLocation, final InputStream in) throws IOException { if (in != null) { return new URLConnection(null) { /** * @throws IOException */ public void connect() throws IOException { connected = true; } /** * @throws IOException */ public InputStream getInputStream() throws IOException { return (in); } }; } if (module == null) { if (bundleLocation == null) { throw new IllegalArgumentException("Module and location cannot be null"); //$NON-NLS-1$ } return getContentConnection(bundleLocation); } return getContentConnection(getUpdateLocation(module)); } private String getUpdateLocation(final Module module) { if (System.getSecurityManager() == null) return getUpdateLocation0(module); return AccessController.doPrivileged(new PrivilegedAction() { public String run() { return getUpdateLocation0(module); } }); } String getUpdateLocation0(Module module) { ModuleRevision current = module.getCurrentRevision(); Generation generation = (Generation) current.getRevisionInfo(); String updateLocation = generation.getHeaders().get(Constants.BUNDLE_UPDATELOCATION); if (updateLocation == null) { updateLocation = module.getLocation(); } if (updateLocation.startsWith(INITIAL_LOCATION)) { updateLocation = updateLocation.substring(INITIAL_LOCATION.length()); } return updateLocation; } private URLConnection getContentConnection(final String spec) throws IOException { if (System.getSecurityManager() == null) { return LocationHelper.getConnection(createURL(spec)); } try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public URLConnection run() throws IOException { return LocationHelper.getConnection(createURL(spec)); } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof IOException) throw (IOException) e.getException(); throw (RuntimeException) e.getException(); } } URL createURL(String spec) throws MalformedURLException { if (spec.startsWith(URLStreamHandlerFactoryImpl.PROTOCOL_REFERENCE)) { return new URL(null, spec, new Handler(equinoxContainer.getConfiguration().getConfiguration(EquinoxLocations.PROP_INSTALL_AREA))); } return new URL(spec); } public Generation install(Module origin, String bundleLocation, URLConnection content) throws BundleException { if (osgiLocation.isReadOnly()) { throw new BundleException("The framework storage area is read only.", BundleException.INVALID_OPERATION); //$NON-NLS-1$ } URL sourceURL = content.getURL(); InputStream in; try { in = content.getInputStream(); } catch (Throwable e) { throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ } // Check if the bundle already exists at this location // before doing the staging and generation creation. // This is important since some installers seem to continually // re-install bundles using the same location each startup Module existingLocation = moduleContainer.getModule(bundleLocation); if (existingLocation != null) { // NOTE this same logic is also in the ModuleContainer // This is necessary because the container does the location locking. // Another thread could win the location lock and install before this thread does. try { in.close(); } catch (IOException e) { // ignore } if (origin != null) { // Check that the existing location is visible from the origin module Bundle bundle = origin.getBundle(); BundleContext context = bundle == null ? null : bundle.getBundleContext(); if (context != null && context.getBundle(existingLocation.getId()) == null) { Bundle b = existingLocation.getBundle(); throw new BundleException(NLS.bind(Msg.ModuleContainer_NameCollisionWithLocation, new Object[] {b.getSymbolicName(), b.getVersion(), bundleLocation}), BundleException.REJECTED_BY_HOOK); } } return (Generation) existingLocation.getCurrentRevision().getRevisionInfo(); } boolean isReference = in instanceof ReferenceInputStream; File staged = stageContent(in, sourceURL); Generation generation = null; try { Long nextID = moduleDatabase.getAndIncrementNextId(); BundleInfo info = new BundleInfo(this, nextID, bundleLocation, 0); generation = info.createGeneration(); File contentFile = getContentFile(staged, isReference, nextID, generation.getGenerationId()); generation.setContent(contentFile, isReference); // Check that we can open the bundle file generation.getBundleFile().open(); setStorageHooks(generation); ModuleRevisionBuilder builder = getBuilder(generation); builder.setId(nextID); Module m = moduleContainer.install(origin, bundleLocation, builder, generation); if (!nextID.equals(m.getId())) { // this revision is already installed. delete the generation generation.delete(); return (Generation) m.getCurrentRevision().getRevisionInfo(); } return generation; } catch (Throwable t) { if (!isReference) { try { delete(staged); } catch (IOException e) { // tried our best } } if (generation != null) { generation.delete(); generation.getBundleInfo().delete(); } if (t instanceof SecurityException) { // TODO hack from ModuleContainer // if the cause is a bundle exception then throw that if (t.getCause() instanceof BundleException) { throw (BundleException) t.getCause(); } throw (SecurityException) t; } if (t instanceof BundleException) { throw (BundleException) t; } throw new BundleException("Error occurred installing a bundle.", t); //$NON-NLS-1$ } finally { if (generation != null) { generation.getBundleInfo().unlockGeneration(generation); } } } private void setStorageHooks(Generation generation) throws BundleException { if (generation.getBundleInfo().getBundleId() == 0) { return; // ignore system bundle } List> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); List> hooks = new ArrayList<>(factories.size()); for (Iterator> iFactories = factories.iterator(); iFactories.hasNext();) { @SuppressWarnings("unchecked") StorageHookFactory> next = (StorageHookFactory>) iFactories.next(); StorageHook hook = next.createStorageHookAndValidateFactoryClass(generation); hooks.add(hook); } generation.setStorageHooks(Collections.unmodifiableList(hooks), true); for (StorageHook hook : hooks) { hook.initialize(generation.getHeaders()); } } public ModuleRevisionBuilder getBuilder(Generation generation) throws BundleException { Dictionary headers = generation.getHeaders(); Map mapHeaders; if (headers instanceof Map) { @SuppressWarnings("unchecked") Map unchecked = (Map) headers; mapHeaders = unchecked; } else { mapHeaders = new HashMap<>(); for (Enumeration eKeys = headers.keys(); eKeys.hasMoreElements();) { String key = eKeys.nextElement(); mapHeaders.put(key, headers.get(key)); } } if (generation.getBundleInfo().getBundleId() != 0) { ModuleRevisionBuilder builder = allowRestrictedProvides ? OSGiManifestBuilderFactory.createBuilder(mapHeaders, null, null, "") : OSGiManifestBuilderFactory.createBuilder(mapHeaders); //$NON-NLS-1$ if ((builder.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) { for (ModuleRevisionBuilder.GenericInfo reqInfo : builder.getRequirements()) { if (HostNamespace.HOST_NAMESPACE.equals(reqInfo.getNamespace())) { if (HostNamespace.EXTENSION_BOOTCLASSPATH.equals(reqInfo.getDirectives().get(HostNamespace.REQUIREMENT_EXTENSION_DIRECTIVE))) { throw new BundleException("Boot classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$ } } } } return builder; } // First we must make sure the VM profile has been loaded loadVMProfile(generation); // dealing with system bundle find the extra capabilities and exports String extraCapabilities = getSystemExtraCapabilities(); String extraExports = getSystemExtraPackages(); return OSGiManifestBuilderFactory.createBuilder(mapHeaders, Constants.SYSTEM_BUNDLE_SYMBOLICNAME, extraExports, extraCapabilities); } private String getSystemExtraCapabilities() { EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); StringBuilder result = new StringBuilder(); String systemCapabilities = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES); if (systemCapabilities != null && systemCapabilities.trim().length() > 0) { result.append(systemCapabilities).append(", "); //$NON-NLS-1$ } String extraSystemCapabilities = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA); if (extraSystemCapabilities != null && extraSystemCapabilities.trim().length() > 0) { result.append(extraSystemCapabilities).append(", "); //$NON-NLS-1$ } result.append(EclipsePlatformNamespace.ECLIPSE_PLATFORM_NAMESPACE).append("; "); //$NON-NLS-1$ result.append(EquinoxConfiguration.PROP_OSGI_OS).append("=").append(equinoxConfig.getOS()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ result.append(EquinoxConfiguration.PROP_OSGI_WS).append("=").append(equinoxConfig.getWS()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ result.append(EquinoxConfiguration.PROP_OSGI_ARCH).append("=").append(equinoxConfig.getOSArch()).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ result.append(EquinoxConfiguration.PROP_OSGI_NL).append("=").append(equinoxConfig.getNL()); //$NON-NLS-1$ String osName = equinoxConfig.getConfiguration(Constants.FRAMEWORK_OS_NAME); osName = osName == null ? null : osName.toLowerCase(); String processor = equinoxConfig.getConfiguration(Constants.FRAMEWORK_PROCESSOR); processor = processor == null ? null : processor.toLowerCase(); String osVersion = equinoxConfig.getConfiguration(Constants.FRAMEWORK_OS_VERSION); osVersion = osVersion == null ? null : osVersion.toLowerCase(); String language = equinoxConfig.getConfiguration(Constants.FRAMEWORK_LANGUAGE); language = language == null ? null : language.toLowerCase(); result.append(", "); //$NON-NLS-1$ result.append(NativeNamespace.NATIVE_NAMESPACE).append("; "); //$NON-NLS-1$ if (osName != null) { osName = getAliasList(equinoxConfig.getAliasMapper().getOSNameAliases(osName)); result.append(NativeNamespace.CAPABILITY_OSNAME_ATTRIBUTE).append(":List=").append(osName).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ } if (processor != null) { processor = getAliasList(equinoxConfig.getAliasMapper().getProcessorAliases(processor)); result.append(NativeNamespace.CAPABILITY_PROCESSOR_ATTRIBUTE).append(":List=").append(processor).append("; "); //$NON-NLS-1$ //$NON-NLS-2$ } result.append(NativeNamespace.CAPABILITY_OSVERSION_ATTRIBUTE).append(":Version").append("=\"").append(osVersion).append("\"; "); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ result.append(NativeNamespace.CAPABILITY_LANGUAGE_ATTRIBUTE).append("=\"").append(language).append('\"'); //$NON-NLS-1$ return result.toString(); } String getAliasList(Collection aliases) { if (aliases.isEmpty()) { return null; } StringBuilder builder = new StringBuilder(); builder.append('"'); for (String alias : aliases) { builder.append(alias).append(','); } builder.setLength(builder.length() - 1); builder.append('"'); return builder.toString(); } private String getSystemExtraPackages() { EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); StringBuilder result = new StringBuilder(); String systemPackages = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES); if (systemPackages != null) { result.append(systemPackages); } String extraSystemPackages = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA); if (extraSystemPackages != null && extraSystemPackages.trim().length() > 0) { if (result.length() > 0) { result.append(", "); //$NON-NLS-1$ } result.append(extraSystemPackages); } return result.toString(); } private void refresh(Module module) throws BundleException { ModuleRevision current = module.getCurrentRevision(); Generation currentGen = (Generation) current.getRevisionInfo(); File content = currentGen.getContent(); String spec = (currentGen.isReference() ? "reference:" : "") + content.toURI().toString(); //$NON-NLS-1$ //$NON-NLS-2$ URLConnection contentConn; try { contentConn = getContentConnection(spec); } catch (IOException e) { throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ } update(module, contentConn); } public Generation update(Module module, URLConnection content) throws BundleException { if (osgiLocation.isReadOnly()) { throw new BundleException("The framework storage area is read only.", BundleException.INVALID_OPERATION); //$NON-NLS-1$ } URL sourceURL = content.getURL(); InputStream in; try { in = content.getInputStream(); } catch (Throwable e) { throw new BundleException("Error reading bundle content.", e); //$NON-NLS-1$ } boolean isReference = in instanceof ReferenceInputStream; File staged = stageContent(in, sourceURL); ModuleRevision current = module.getCurrentRevision(); Generation currentGen = (Generation) current.getRevisionInfo(); BundleInfo bundleInfo = currentGen.getBundleInfo(); Generation newGen = bundleInfo.createGeneration(); try { File contentFile = getContentFile(staged, isReference, bundleInfo.getBundleId(), newGen.getGenerationId()); newGen.setContent(contentFile, isReference); // Check that we can open the bundle file newGen.getBundleFile().open(); setStorageHooks(newGen); ModuleRevisionBuilder builder = getBuilder(newGen); moduleContainer.update(module, builder, newGen); } catch (Throwable t) { if (!isReference) { try { delete(staged); } catch (IOException e) { // tried our best } } newGen.delete(); if (t instanceof SecurityException) { // TODO hack from ModuleContainer // if the cause is a bundle exception then throw that if (t.getCause() instanceof BundleException) { throw (BundleException) t.getCause(); } throw (SecurityException) t; } if (t instanceof BundleException) { throw (BundleException) t; } throw new BundleException("Error occurred installing a bundle.", t); //$NON-NLS-1$ } finally { bundleInfo.unlockGeneration(newGen); } return newGen; } private File getContentFile(final File staged, final boolean isReference, final long bundleID, final long generationID) throws BundleException { if (System.getSecurityManager() == null) return getContentFile0(staged, isReference, bundleID, generationID); try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { public File run() throws BundleException { return getContentFile0(staged, isReference, bundleID, generationID); } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof BundleException) throw (BundleException) e.getException(); throw (RuntimeException) e.getException(); } } File getContentFile0(File staged, boolean isReference, long bundleID, long generationID) throws BundleException { File contentFile; if (!isReference) { File generationRoot = new File(childRoot, bundleID + "/" + generationID); //$NON-NLS-1$ generationRoot.mkdirs(); if (!generationRoot.isDirectory()) { throw new BundleException("Could not create generation directory: " + generationRoot.getAbsolutePath()); //$NON-NLS-1$ } contentFile = new File(generationRoot, BUNDLE_FILE_NAME); if (!StorageUtil.move(staged, contentFile, getConfiguration().getDebug().DEBUG_STORAGE)) { throw new BundleException("Error while renaming bundle file to final location: " + contentFile); //$NON-NLS-1$ } } else { contentFile = staged; } return contentFile; } private static String getBundleFilePath(long bundleID, long generationID) { return bundleID + "/" + generationID + "/" + BUNDLE_FILE_NAME; //$NON-NLS-1$ //$NON-NLS-2$ } public File getFile(String path, boolean checkParent) { // first check the child location File childPath = new File(childRoot, path); // now check the parent if (checkParent && parentRoot != null) { if (childPath.exists()) { return childPath; } File parentPath = new File(parentRoot, path); if (parentPath.exists()) { // only use the parent file only if it exists; return parentPath; } } // did not exist in both locations; use the child path return childPath; } private File stageContent(final InputStream in, final URL sourceURL) throws BundleException { if (System.getSecurityManager() == null) return stageContent0(in, sourceURL); try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { public File run() throws BundleException { return stageContent0(in, sourceURL); } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof BundleException) throw (BundleException) e.getException(); throw (RuntimeException) e.getException(); } } File stageContent0(InputStream in, URL sourceURL) throws BundleException { File outFile = null; try { if (in instanceof ReferenceInputStream) { URL reference = ((ReferenceInputStream) in).getReference(); if (!"file".equals(reference.getProtocol())) //$NON-NLS-1$ throw new BundleException(NLS.bind(Msg.ADAPTOR_URL_CREATE_EXCEPTION, reference)); return new File(reference.getPath()); } outFile = File.createTempFile(BUNDLE_FILE_NAME, ".tmp", childRoot); //$NON-NLS-1$ String protocol = sourceURL == null ? null : sourceURL.getProtocol(); if ("file".equals(protocol)) { //$NON-NLS-1$ File inFile = new File(sourceURL.getPath()); inFile = LocationHelper.decodePath(inFile); if (inFile.isDirectory()) { // need to delete the outFile because it is not a directory outFile.delete(); StorageUtil.copyDir(inFile, outFile); } else { StorageUtil.readFile(in, outFile); } } else { StorageUtil.readFile(in, outFile); } return outFile; } catch (IOException e) { if (outFile != null) { outFile.delete(); } throw new BundleException(Msg.BUNDLE_READ_EXCEPTION, BundleException.READ_ERROR, e); } } /** * Attempts to set the permissions of the file in a system dependent way. * @param file the file to set the permissions on */ public void setPermissions(File file) { String commandProp = getConfiguration().getConfiguration(EquinoxConfiguration.PROP_SETPERMS_CMD); if (commandProp == null) commandProp = getConfiguration().getConfiguration(Constants.FRAMEWORK_EXECPERMISSION); if (commandProp == null) return; String[] temp = ManifestElement.getArrayFromList(commandProp, " "); //$NON-NLS-1$ List command = new ArrayList<>(temp.length + 1); boolean foundFullPath = false; for (int i = 0; i < temp.length; i++) { if ("[fullpath]".equals(temp[i]) || "${abspath}".equals(temp[i])) { //$NON-NLS-1$ //$NON-NLS-2$ command.add(file.getAbsolutePath()); foundFullPath = true; } else command.add(temp[i]); } if (!foundFullPath) command.add(file.getAbsolutePath()); try { Runtime.getRuntime().exec(command.toArray(new String[command.size()])).waitFor(); } catch (Exception e) { e.printStackTrace(); } } public BundleFile createBundleFile(File content, Generation generation, boolean isDirectory, boolean isBase) { BundleFile result; try { if (isDirectory) { boolean strictPath = Boolean.parseBoolean(equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROPERTY_STRICT_BUNDLE_ENTRY_PATH, Boolean.FALSE.toString())); result = new DirBundleFile(content, strictPath); } else { result = new ZipBundleFile(content, generation, mruList, getConfiguration().getDebug()); } } catch (IOException e) { throw new RuntimeException("Could not create bundle file.", e); //$NON-NLS-1$ } return wrapBundleFile(result, generation, isBase); } public BundleFile createNestedBundleFile(String nestedDir, BundleFile bundleFile, Generation generation) { return createNestedBundleFile(nestedDir, bundleFile, generation, Collections. emptyList()); } public BundleFile createNestedBundleFile(String nestedDir, BundleFile bundleFile, Generation generation, Collection filterPrefixes) { // here we assume the content is a path offset into the base bundle file; create a NestedDirBundleFile return wrapBundleFile(new NestedDirBundleFile(bundleFile, nestedDir, filterPrefixes), generation, false); } public BundleFile wrapBundleFile(BundleFile bundleFile, Generation generation, boolean isBase) { // try creating a wrapper bundlefile out of it. List wrapperFactories = getConfiguration().getHookRegistry().getBundleFileWrapperFactoryHooks(); BundleFileWrapperChain wrapped = wrapperFactories.isEmpty() ? null : new BundleFileWrapperChain(bundleFile, null); for (BundleFileWrapperFactoryHook wrapperFactory : wrapperFactories) { BundleFileWrapper wrapperBundle = wrapperFactory.wrapBundleFile(bundleFile, generation, isBase); if (wrapperBundle != null && wrapperBundle != bundleFile) bundleFile = wrapped = new BundleFileWrapperChain(wrapperBundle, wrapped); } return bundleFile; } public void compact() { if (!osgiLocation.isReadOnly()) { compact(childRoot); } } private void compact(File directory) { if (getConfiguration().getDebug().DEBUG_STORAGE) Debug.println("compact(" + directory.getPath() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ String list[] = directory.list(); if (list == null) return; int len = list.length; for (int i = 0; i < len; i++) { if (BUNDLE_DATA_DIR.equals(list[i])) continue; /* do not examine the bundles data dir. */ File target = new File(directory, list[i]); // if the file is a directory if (!target.isDirectory()) continue; File delete = new File(target, DELETE_FLAG); // and the directory is marked for delete if (delete.exists()) { // if rm fails to delete the directory and .delete was removed if (!StorageUtil.rm(target, getConfiguration().getDebug().DEBUG_STORAGE) && !delete.exists()) { try { // recreate .delete FileOutputStream out = new FileOutputStream(delete); out.close(); } catch (IOException e) { if (getConfiguration().getDebug().DEBUG_STORAGE) Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } } } else { compact(target); /* descend into directory */ } } } void delete(final File delete) throws IOException { if (System.getSecurityManager() == null) { delete0(delete); } else { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Void run() throws IOException { delete0(delete); return null; } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof IOException) throw (IOException) e.getException(); throw (RuntimeException) e.getException(); } } } void delete0(File delete) throws IOException { if (!StorageUtil.rm(delete, getConfiguration().getDebug().DEBUG_STORAGE)) { /* create .delete */ FileOutputStream out = new FileOutputStream(new File(delete, DELETE_FLAG)); out.close(); } } public void save() throws IOException { if (isReadOnly()) { return; } if (System.getSecurityManager() == null) { save0(); } else { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Void run() throws IOException { save0(); return null; } }); } catch (PrivilegedActionException e) { if (e.getException() instanceof IOException) throw (IOException) e.getException(); throw (RuntimeException) e.getException(); } } } void save0() throws IOException { StorageManager childStorageManager = null; ManagedOutputStream mos = null; DataOutputStream out = null; boolean success = false; moduleDatabase.readLock(); try { synchronized (this.saveMonitor) { if (lastSavedTimestamp == moduleDatabase.getTimestamp()) return; childStorageManager = getChildStorageManager(); mos = childStorageManager.getOutputStream(FRAMEWORK_INFO); out = new DataOutputStream(new BufferedOutputStream(mos)); saveGenerations(out); savePermissionData(out); moduleDatabase.store(out, true); lastSavedTimestamp = moduleDatabase.getTimestamp(); success = true; } } finally { if (!success) { if (mos != null) { mos.abort(); } } if (out != null) { try { out.close(); } catch (IOException e) { // tried our best } } if (childStorageManager != null) { childStorageManager.close(); } moduleDatabase.readUnlock(); } } private void savePermissionData(DataOutputStream out) throws IOException { permissionData.savePermissionData(out); } private void saveGenerations(DataOutputStream out) throws IOException { List modules = moduleContainer.getModules(); List generations = new ArrayList<>(); for (Module module : modules) { ModuleRevision revision = module.getCurrentRevision(); if (revision != null) { Generation generation = (Generation) revision.getRevisionInfo(); if (generation != null) { generations.add(generation); } } } out.writeInt(VERSION); out.writeUTF(runtimeVersion.toString()); out.writeInt(cachedHeaderKeys.size()); for (String headerKey : cachedHeaderKeys) { out.writeUTF(headerKey); } out.writeInt(generations.size()); for (Generation generation : generations) { BundleInfo bundleInfo = generation.getBundleInfo(); out.writeLong(bundleInfo.getBundleId()); out.writeUTF(bundleInfo.getLocation()); out.writeLong(bundleInfo.getNextGenerationId()); out.writeLong(generation.getGenerationId()); out.writeBoolean(generation.isDirectory()); out.writeBoolean(generation.isReference()); out.writeBoolean(generation.hasPackageInfo()); if (bundleInfo.getBundleId() == 0) { // just write empty string for system bundle content in this case out.writeUTF(""); //$NON-NLS-1$ } else { if (generation.isReference()) { // make reference installs relative to the install path out.writeUTF(new FilePath(installPath).makeRelative(new FilePath(generation.getContent().getAbsolutePath()))); } else { // make normal installs relative to the storage area out.writeUTF(Storage.getBundleFilePath(bundleInfo.getBundleId(), generation.getGenerationId())); } } out.writeLong(generation.getLastModified()); Dictionary headers = generation.getHeaders(); for (String headerKey : cachedHeaderKeys) { String value = headers.get(headerKey); if (value != null) { out.writeUTF(value); } else { out.writeUTF(NUL); } } out.writeBoolean(generation.isMRJar()); } saveStorageHookData(out, generations); } private void saveStorageHookData(DataOutputStream out, List generations) throws IOException { List> factories = getConfiguration().getHookRegistry().getStorageHookFactories(); out.writeInt(factories.size()); for (StorageHookFactory factory : factories) { out.writeUTF(factory.getKey()); out.writeInt(factory.getStorageVersion()); // create a temporary in memory stream so we can figure out the length ByteArrayOutputStream tempBytes = new ByteArrayOutputStream(); DataOutputStream temp = new DataOutputStream(tempBytes); try { Object saveContext = factory.createSaveContext(); for (Generation generation : generations) { if (generation.getBundleInfo().getBundleId() == 0) { continue; // ignore system bundle } @SuppressWarnings({"rawtypes", "unchecked"}) StorageHook hook = generation.getStorageHook((Class) factory.getClass()); hook.save(saveContext, temp); } } finally { temp.close(); } out.writeInt(tempBytes.size()); out.write(tempBytes.toByteArray()); } } private Map loadGenerations(DataInputStream in) throws IOException { if (in == null) { return new HashMap<>(0); } int version = in.readInt(); if (version > VERSION || version < LOWEST_VERSION_SUPPORTED) { throw new IllegalArgumentException("Found persistent version \"" + version + "\" expecting \"" + VERSION + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } Version savedRuntimeVersion = (version >= MR_JAR_VERSION) ? Version.parseVersion(in.readUTF()) : null; if (savedRuntimeVersion == null || !savedRuntimeVersion.equals(runtimeVersion)) { refreshMRBundles.set(true); } int numCachedHeaders = in.readInt(); List storedCachedHeaderKeys = new ArrayList<>(numCachedHeaders); for (int i = 0; i < numCachedHeaders; i++) { storedCachedHeaderKeys.add(ObjectPool.intern(in.readUTF())); } int numInfos = in.readInt(); Map result = new HashMap<>(numInfos); List generations = new ArrayList<>(numInfos); for (int i = 0; i < numInfos; i++) { long infoId = in.readLong(); String infoLocation = ObjectPool.intern(in.readUTF()); long nextGenId = in.readLong(); long generationId = in.readLong(); boolean isDirectory = in.readBoolean(); boolean isReference = in.readBoolean(); boolean hasPackageInfo = in.readBoolean(); String contentPath = in.readUTF(); long lastModified = in.readLong(); Map cachedHeaders = new HashMap<>(storedCachedHeaderKeys.size()); for (String headerKey : storedCachedHeaderKeys) { String value = in.readUTF(); if (NUL.equals(value)) { value = null; } else { value = ObjectPool.intern(value); } cachedHeaders.put(headerKey, value); } boolean isMRJar = (version >= MR_JAR_VERSION) ? in.readBoolean() : false; File content; if (infoId == 0) { content = getSystemContent(); isDirectory = content != null ? content.isDirectory() : false; // Note that we do not do any checking for absolute paths with // the system bundle. We always take the content as discovered // by getSystemContent() } else { content = new File(contentPath); if (!content.isAbsolute()) { // make sure it has the absolute location instead if (isReference) { // reference installs are relative to the installPath content = new File(installPath, contentPath); } else { // normal installs are relative to the storage area content = getFile(contentPath, true); } } } BundleInfo info = new BundleInfo(this, infoId, infoLocation, nextGenId); Generation generation = info.restoreGeneration(generationId, content, isDirectory, isReference, hasPackageInfo, cachedHeaders, lastModified, isMRJar); result.put(infoId, generation); generations.add(generation); } loadStorageHookData(generations, in); return result; } private void loadStorageHookData(List generations, DataInputStream in) throws IOException { List> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); Map>> hookMap = new HashMap<>(); int numFactories = in.readInt(); for (int i = 0; i < numFactories; i++) { String factoryName = in.readUTF(); int version = in.readInt(); StorageHookFactory> factory = null; for (Iterator> iFactories = factories.iterator(); iFactories.hasNext();) { @SuppressWarnings("unchecked") StorageHookFactory> next = (StorageHookFactory>) iFactories.next(); if (next.getKey().equals(factoryName)) { factory = next; iFactories.remove(); break; } } int dataSize = in.readInt(); byte[] bytes = new byte[dataSize]; in.readFully(bytes); if (factory != null) { DataInputStream temp = new DataInputStream(new ByteArrayInputStream(bytes)); try { if (factory.isCompatibleWith(version)) { Object loadContext = factory.createLoadContext(version); for (Generation generation : generations) { if (generation.getBundleInfo().getBundleId() == 0) { continue; // ignore system bundle } StorageHook hook = factory.createStorageHookAndValidateFactoryClass(generation); hook.load(loadContext, temp); getHooks(hookMap, generation).add(hook); } } else { // recover by reinitializing the hook for (Generation generation : generations) { if (generation.getBundleInfo().getBundleId() == 0) { continue; // ignore system bundle } StorageHook hook = factory.createStorageHookAndValidateFactoryClass(generation); hook.initialize(generation.getHeaders()); getHooks(hookMap, generation).add(hook); } } } catch (BundleException e) { throw new IOException(e); } finally { temp.close(); } } } // now we need to recover for any hooks that are left for (Iterator> iFactories = factories.iterator(); iFactories.hasNext();) { @SuppressWarnings("unchecked") StorageHookFactory> next = (StorageHookFactory>) iFactories.next(); // recover by reinitializing the hook for (Generation generation : generations) { if (generation.getBundleInfo().getBundleId() == 0) { continue; // ignore system bundle } StorageHook hook = next.createStorageHookAndValidateFactoryClass(generation); try { hook.initialize(generation.getHeaders()); getHooks(hookMap, generation).add(hook); } catch (BundleException e) { throw new IOException(e); } } } // now set the hooks to the generations for (Generation generation : generations) { generation.setStorageHooks(Collections.unmodifiableList(getHooks(hookMap, generation)), false); } } private static List> getHooks(Map>> hookMap, Generation generation) { List> result = hookMap.get(generation); if (result == null) { result = new ArrayList<>(); hookMap.put(generation, result); } return result; } private File getSystemContent() { String frameworkValue = equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_FRAMEWORK); if (frameworkValue == null || !frameworkValue.startsWith("file:")) { //$NON-NLS-1$ return null; } // TODO assumes the location is a file URL File result = new File(frameworkValue.substring(5)); if (!result.exists()) { throw new IllegalStateException("Configured framework location does not exist: " + result.getAbsolutePath()); //$NON-NLS-1$ } return result; } private void loadVMProfile(Generation systemGeneration) { EquinoxConfiguration equinoxConfig = equinoxContainer.getConfiguration(); Properties profileProps = findVMProfile(systemGeneration); String systemExports = equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES); // set the system exports property using the vm profile; only if the property is not already set if (systemExports == null) { systemExports = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES); if (systemExports != null) equinoxConfig.setConfiguration(Constants.FRAMEWORK_SYSTEMPACKAGES, systemExports); } // set the org.osgi.framework.bootdelegation property according to the java profile String type = equinoxConfig.getConfiguration(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_BOOTDELEGATION); // a null value means ignore String profileBootDelegation = profileProps.getProperty(Constants.FRAMEWORK_BOOTDELEGATION); if (EquinoxConfiguration.PROP_OSGI_BOOTDELEGATION_OVERRIDE.equals(type)) { if (profileBootDelegation == null) equinoxConfig.clearConfiguration(Constants.FRAMEWORK_BOOTDELEGATION); // override with a null value else equinoxConfig.setConfiguration(Constants.FRAMEWORK_BOOTDELEGATION, profileBootDelegation); // override with the profile value } else if (EquinoxConfiguration.PROP_OSGI_BOOTDELEGATION_NONE.equals(type)) equinoxConfig.clearConfiguration(Constants.FRAMEWORK_BOOTDELEGATION); // remove the bootdelegation property in case it was set // set the org.osgi.framework.executionenvironment property according to the java profile if (equinoxConfig.getConfiguration(Constants.FRAMEWORK_EXECUTIONENVIRONMENT) == null) { // get the ee from the java profile; if no ee is defined then try the java profile name String ee = profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, profileProps.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME)); if (ee != null) equinoxConfig.setConfiguration(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); } // set the org.osgi.framework.system.capabilities property according to the java profile if (equinoxConfig.getConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES) == null) { String systemCapabilities = profileProps.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES); if (systemCapabilities != null) equinoxConfig.setConfiguration(Constants.FRAMEWORK_SYSTEMCAPABILITIES, systemCapabilities); } } private Properties findVMProfile(Generation systemGeneration) { Properties result = readConfiguredJavaProfile(systemGeneration); String vmProfile = null; try { if (result != null) { return result; } if (Version.valueOf("9").compareTo(runtimeVersion) <= 0) { //$NON-NLS-1$ result = calculateVMProfile(runtimeVersion); if (result != null) { return result; } // could not calculate; fall back to reading profile files } String embeddedProfileName = "-"; //$NON-NLS-1$ // If javaSE 1.8 then check for release file for profile name. if (runtimeVersion != null && Version.valueOf("1.8").compareTo(runtimeVersion) <= 0) { //$NON-NLS-1$ String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ if (javaHome != null) { File release = new File(javaHome, "release"); //$NON-NLS-1$ if (release.exists()) { Properties releaseProps = new Properties(); try (InputStream releaseStream = new FileInputStream(release)) { releaseProps.load(releaseStream); String releaseName = releaseProps.getProperty("JAVA_PROFILE"); //$NON-NLS-1$ if (releaseName != null) { // make sure to remove extra quotes releaseName = releaseName.replaceAll("^\\s*\"?|\"?\\s*$", ""); //$NON-NLS-1$ //$NON-NLS-2$ embeddedProfileName = "_" + releaseName + "-"; //$NON-NLS-1$ //$NON-NLS-2$ } } catch (IOException e) { // ignore } } } } result = new Properties(); vmProfile = JAVASE + embeddedProfileName + javaSpecVersion; InputStream profileIn = null; if (vmProfile != null) { // look for a profile in the system bundle based on the vm profile String javaProfile = vmProfile + PROFILE_EXT; profileIn = findInSystemBundle(systemGeneration, javaProfile); if (profileIn == null) profileIn = getNextBestProfile(systemGeneration, JAVASE, runtimeVersion, embeddedProfileName); } if (profileIn == null) // the profile url is still null then use the min profile the framework can use profileIn = findInSystemBundle(systemGeneration, "JavaSE-1.7.profile"); //$NON-NLS-1$ if (profileIn != null) { try { result.load(new BufferedInputStream(profileIn)); } catch (IOException e) { // TODO consider logging ... } finally { try { profileIn.close(); } catch (IOException ee) { // do nothing } } } } finally { // set the profile name if it does not provide one if (result != null && result.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME) == null) { if (vmProfile != null) { result.put(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME, vmProfile.replace('_', '/')); } else { // last resort; default to the absolute minimum profile name for the framework result.put(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME, "JavaSE-1.7"); //$NON-NLS-1$ } } } return result; } private Properties readConfiguredJavaProfile(Generation systemGeneration) { // check for the java profile property for a url String propJavaProfile = equinoxContainer.getConfiguration().getConfiguration(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE); if (propJavaProfile != null) { InputStream profileIn = null; try { // we assume a URL profileIn = new URL(propJavaProfile).openStream(); } catch (IOException e) { // try using a relative path in the system bundle profileIn = findInSystemBundle(systemGeneration, propJavaProfile); } if (profileIn != null) { Properties result = new Properties(); try { result.load(new BufferedInputStream(profileIn)); } catch (IOException e) { // consider logging } finally { try { profileIn.close(); } catch (IOException e) { // nothing to do } } return result; } } return null; } private Properties calculateVMProfile(Version javaVersion) { String systemPackages = calculateVMPackages(); if (systemPackages == null) { return null; } String executionEnvs = calculateVMExecutionEnvs(javaVersion); String eeCapabilities = calculateEECapabilities(javaVersion); Properties result = new Properties(); result.put(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackages); result.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, executionEnvs); result.put(Constants.FRAMEWORK_SYSTEMCAPABILITIES, eeCapabilities); return result; } private String calculateVMExecutionEnvs(Version javaVersion) { 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$ Version v = new Version(9, 0, 0); while (v.compareTo(javaVersion) <= 0) { result.append(',').append(' ').append(JAVASE).append('-').append(v.getMajor()); if (v.getMinor() > 0) { result.append('.').append(v.getMinor()); } if (v.getMajor() == javaVersion.getMajor()) { v = new Version(v.getMajor(), v.getMinor() + 1, 0); } else { v = new Version(v.getMajor() + 1, 0, 0); } } return result.toString(); } private String calculateEECapabilities(Version javaVersion) { Version v = new Version(9, 0, 0); StringBuilder versionsBulder = new StringBuilder(); while (v.compareTo(javaVersion) <= 0) { versionsBulder.append(',').append(' ').append(v.getMajor()).append('.').append(v.getMinor()); if (v.getMajor() == javaVersion.getMajor()) { v = new Version(v.getMajor(), v.getMinor() + 1, 0); } else { v = new Version(v.getMajor() + 1, 0, 0); } } String versionsList = versionsBulder.toString(); StringBuilder result = new StringBuilder("osgi.ee; osgi.ee=\"OSGi/Minimum\"; version:List=\"1.0, 1.1, 1.2\", osgi.ee; osgi.ee=\"JRE\"; version:List=\"1.0, 1.1\", osgi.ee; osgi.ee=\"JavaSE\"; version:List=\"1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8"); //$NON-NLS-1$ result.append(versionsList).append("\""); //$NON-NLS-1$ result.append(",osgi.ee; osgi.ee=\"JavaSE/compact1\"; version:List=\"1.8"); //$NON-NLS-1$ result.append(versionsList).append("\""); //$NON-NLS-1$ result.append(",osgi.ee; osgi.ee=\"JavaSE/compact2\"; version:List=\"1.8"); //$NON-NLS-1$ result.append(versionsList).append("\""); //$NON-NLS-1$ result.append(",osgi.ee; osgi.ee=\"JavaSE/compact3\"; version:List=\"1.8"); //$NON-NLS-1$ result.append(versionsList).append("\""); //$NON-NLS-1$ return result.toString(); } private String calculateVMPackages() { try { List packages = new ArrayList<>(); Class moduleLayerClass = Class.forName("java.lang.ModuleLayer"); //$NON-NLS-1$ Method boot = moduleLayerClass.getMethod("boot"); //$NON-NLS-1$ Method modules = moduleLayerClass.getMethod("modules"); //$NON-NLS-1$ Class moduleClass = Class.forName("java.lang.Module"); //$NON-NLS-1$ Method getDescriptor = moduleClass.getMethod("getDescriptor"); //$NON-NLS-1$ Class moduleDescriptorClass = Class.forName("java.lang.module.ModuleDescriptor"); //$NON-NLS-1$ Method exports = moduleDescriptorClass.getMethod("exports"); //$NON-NLS-1$ Class exportsClass = Class.forName("java.lang.module.ModuleDescriptor$Exports"); //$NON-NLS-1$ Method targets = exportsClass.getMethod("targets"); //$NON-NLS-1$ Method source = exportsClass.getMethod("source"); //$NON-NLS-1$ Object bootLayer = boot.invoke(null); Set bootModules = (Set) modules.invoke(bootLayer); for (Object m : bootModules) { Object descriptor = getDescriptor.invoke(m); for (Object export : (Set) exports.invoke(descriptor)) { String pkg = (String) source.invoke(export); if (((Set) targets.invoke(export)).isEmpty() && !pkg.startsWith("java.")) { //$NON-NLS-1$ packages.add(pkg); } } } Collections.sort(packages); StringBuilder result = new StringBuilder(); for (String pkg : packages) { if (result.length() != 0) { result.append(',').append(' '); } result.append(pkg); } return result.toString(); } catch (Exception e) { equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, "Error determining system packages.", e); //$NON-NLS-1$ return null; } } private InputStream getNextBestProfile(Generation systemGeneration, String javaEdition, Version javaVersion, String embeddedProfileName) { if (javaVersion == null || javaEdition != JAVASE) return null; // we cannot automatically choose the next best profile unless this is a JavaSE vm InputStream bestProfile = findNextBestProfile(systemGeneration, javaEdition, javaVersion, embeddedProfileName); if (bestProfile == null && !"-".equals(embeddedProfileName)) { //$NON-NLS-1$ // Just use the base javaEdition name without the profile name as backup return getNextBestProfile(systemGeneration, javaEdition, javaVersion, "-"); //$NON-NLS-1$ } return bestProfile; } private InputStream findNextBestProfile(Generation systemGeneration, String javaEdition, Version javaVersion, String embeddedProfileName) { InputStream result = null; int major = javaVersion.getMajor(); int minor = javaVersion.getMinor(); do { // If minor is zero then it is not included in the name String profileResourceName = javaEdition + embeddedProfileName + major + ((minor > 0) ? "." + minor : "") + PROFILE_EXT; //$NON-NLS-1$ //$NON-NLS-2$ result = findInSystemBundle(systemGeneration, profileResourceName); if (minor > 0) { minor -= 1; } else if (major > 9) { major -= 1; } else if (major <= 9 && major > 1) { minor = 8; major = 1; } else { // we have reached the end of our search; return the existing result; return result; } } while (result == null && minor >= 0); return result; } private InputStream findInSystemBundle(Generation systemGeneration, String entry) { BundleFile systemContent = systemGeneration.getBundleFile(); BundleEntry systemEntry = systemContent != null ? systemContent.getEntry(entry) : null; InputStream result = null; if (systemEntry != null) { try { result = systemEntry.getInputStream(); } catch (IOException e) { // Do nothing } } if (result == null) { // Check the ClassLoader in case we're launched off the Java boot classpath ClassLoader loader = getClass().getClassLoader(); result = loader == null ? ClassLoader.getSystemResourceAsStream(entry) : loader.getResourceAsStream(entry); } return result; } public static Enumeration findEntries(List generations, String path, String filePattern, int options) { List bundleFiles = new ArrayList<>(generations.size()); for (Generation generation : generations) bundleFiles.add(generation.getBundleFile()); // search all the bundle files List pathList = listEntryPaths(bundleFiles, path, filePattern, options); // return null if no entries found if (pathList.size() == 0) return null; // create an enumeration to enumerate the pathList final String[] pathArray = pathList.toArray(new String[pathList.size()]); final Generation[] generationArray = generations.toArray(new Generation[generations.size()]); return new Enumeration() { private int curPathIndex = 0; private int curDataIndex = 0; private URL nextElement = null; public boolean hasMoreElements() { if (nextElement != null) return true; getNextElement(); return nextElement != null; } public URL nextElement() { if (!hasMoreElements()) throw new NoSuchElementException(); URL result = nextElement; // force the next element search getNextElement(); return result; } private void getNextElement() { nextElement = null; if (curPathIndex >= pathArray.length) // reached the end of the pathArray; no more elements return; while (nextElement == null && curPathIndex < pathArray.length) { String curPath = pathArray[curPathIndex]; // search the generation until we have searched them all while (nextElement == null && curDataIndex < generationArray.length) nextElement = generationArray[curDataIndex++].getEntry(curPath); // we have searched all datas then advance to the next path if (curDataIndex >= generationArray.length) { curPathIndex++; curDataIndex = 0; } } } }; } /** * Returns the names of resources available from a list of bundle files. * No duplicate resource names are returned, each name is unique. * @param bundleFiles the list of bundle files to search in * @param path The path name in which to look. * @param filePattern The file name pattern for selecting resource names in * the specified path. * @param options The options for listing resource names. * @return a list of resource names. If no resources are found then * the empty list is returned. * @see BundleWiring#listResources(String, String, int) */ public static List listEntryPaths(List bundleFiles, String path, String filePattern, int options) { // Use LinkedHashSet for optimized performance of contains() plus // ordering guarantees. LinkedHashSet pathList = new LinkedHashSet<>(); Filter patternFilter = null; Hashtable patternProps = null; if (filePattern != null) { // Optimization: If the file pattern does not include a wildcard or escape char then it must represent a single file. // Avoid pattern matching and use BundleFile.getEntry() if recursion was not requested. if ((options & BundleWiring.FINDENTRIES_RECURSE) == 0 && filePattern.indexOf('*') == -1 && filePattern.indexOf('\\') == -1) { if (path.length() == 0) path = filePattern; else path += path.charAt(path.length() - 1) == '/' ? filePattern : '/' + filePattern; for (BundleFile bundleFile : bundleFiles) { if (bundleFile.getEntry(path) != null && !pathList.contains(path)) pathList.add(path); } return new ArrayList<>(pathList); } // For when the file pattern includes a wildcard. try { // create a file pattern filter with 'filename' as the key patternFilter = FilterImpl.newInstance("(filename=" + sanitizeFilterInput(filePattern) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ // create a single hashtable to be shared during the recursive search patternProps = new Hashtable<>(2); } catch (InvalidSyntaxException e) { // TODO something unexpected happened; log error and return nothing // Bundle b = context == null ? null : context.getBundle(); // eventPublisher.publishFrameworkEvent(FrameworkEvent.ERROR, b, e); return new ArrayList<>(pathList); } } // find the entry paths for the datas for (BundleFile bundleFile : bundleFiles) { listEntryPaths(bundleFile, path, patternFilter, patternProps, options, pathList); } return new ArrayList<>(pathList); } public static String sanitizeFilterInput(String filePattern) throws InvalidSyntaxException { StringBuffer buffer = null; boolean foundEscape = false; for (int i = 0; i < filePattern.length(); i++) { char c = filePattern.charAt(i); switch (c) { case '\\' : // we either used the escape found or found a new escape. foundEscape = foundEscape ? false : true; if (buffer != null) buffer.append(c); break; case '(' : case ')' : if (!foundEscape) { if (buffer == null) { buffer = new StringBuffer(filePattern.length() + 16); buffer.append(filePattern.substring(0, i)); } // must escape with '\' buffer.append('\\'); } else { foundEscape = false; // used the escape found } if (buffer != null) buffer.append(c); break; default : // if we found an escape it has been used foundEscape = false; if (buffer != null) buffer.append(c); break; } } if (foundEscape) throw new InvalidSyntaxException("Trailing escape characters must be escaped.", filePattern); //$NON-NLS-1$ return buffer == null ? filePattern : buffer.toString(); } // Use LinkedHashSet for optimized performance of contains() plus ordering // guarantees. private static LinkedHashSet listEntryPaths(BundleFile bundleFile, String path, Filter patternFilter, Hashtable patternProps, int options, LinkedHashSet pathList) { if (pathList == null) pathList = new LinkedHashSet<>(); Enumeration entryPaths; if ((options & BundleWiring.FINDENTRIES_RECURSE) != 0) entryPaths = bundleFile.getEntryPaths(path, true); else entryPaths = bundleFile.getEntryPaths(path); if (entryPaths == null) return pathList; while (entryPaths.hasMoreElements()) { String entry = entryPaths.nextElement(); int lastSlash = entry.lastIndexOf('/'); if (patternProps != null) { int secondToLastSlash = entry.lastIndexOf('/', lastSlash - 1); int fileStart; int fileEnd = entry.length(); if (lastSlash < 0) fileStart = 0; else if (lastSlash != entry.length() - 1) fileStart = lastSlash + 1; else { fileEnd = lastSlash; // leave the lastSlash out if (secondToLastSlash < 0) fileStart = 0; else fileStart = secondToLastSlash + 1; } String fileName = entry.substring(fileStart, fileEnd); // set the filename to the current entry patternProps.put("filename", fileName); //$NON-NLS-1$ } // prevent duplicates and match on the patternFilter if (!pathList.contains(entry) && (patternFilter == null || patternFilter.matchCase(patternProps))) pathList.add(entry); } return pathList; } public String copyToTempLibrary(Generation generation, String absolutePath) { File libTempDir = new File(childRoot, LIB_TEMP); // we assume the absolutePath is a File path File realLib = new File(absolutePath); String libName = realLib.getName(); // find a temp dir for the bundle data and the library; File bundleTempDir = null; File libTempFile = null; // We need a somewhat predictable temp dir for the libraries of a given bundle; // This is not strictly necessary but it does help scenarios where one native library loads another native library without using java. // On some OSes this causes issues because the second library is cannot be found. // This has been worked around by the bundles loading the libraries in a particular order (and setting some LIB_PATH env). // The one catch is that the libraries need to be in the same directory and they must use their original lib names. // // 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 // in cases where the temp dir may already exist. Long bundleID = new Long(generation.getBundleInfo().getBundleId()); for (int i = 0; i < Integer.MAX_VALUE; i++) { bundleTempDir = new File(libTempDir, bundleID.toString() + "_" + Integer.valueOf(i).toString()); //$NON-NLS-1$ libTempFile = new File(bundleTempDir, libName); if (bundleTempDir.exists()) { if (libTempFile.exists()) continue; // to to next temp file break; } break; } if (!bundleTempDir.isDirectory()) { bundleTempDir.mkdirs(); bundleTempDir.deleteOnExit(); // This is just a safeguard incase the VM is terminated unexpectantly, it also looks like deleteOnExit cannot really work because // the VM likely will still have a lock on the lib file at the time of VM exit. File deleteFlag = new File(libTempDir, DELETE_FLAG); if (!deleteFlag.exists()) { // need to create a delete flag to force removal the temp libraries try { FileOutputStream out = new FileOutputStream(deleteFlag); out.close(); } catch (IOException e) { // do nothing; that would mean we did not make the temp dir successfully } } } // copy the library file try { InputStream in = new FileInputStream(realLib); StorageUtil.readFile(in, libTempFile); // set permissions if needed setPermissions(libTempFile); libTempFile.deleteOnExit(); // this probably will not work because the VM will probably have the lib locked at exit // return the temporary path return libTempFile.getAbsolutePath(); } catch (IOException e) { equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, e.getMessage(), e); return null; } } public SecurityAdmin getSecurityAdmin() { return securityAdmin; } protected StorageManager getChildStorageManager() throws IOException { String locking = getConfiguration().getConfiguration(LocationHelper.PROP_OSGI_LOCKING, LocationHelper.LOCKING_NIO); StorageManager sManager = new StorageManager(childRoot, isReadOnly() ? LocationHelper.LOCKING_NONE : locking, isReadOnly()); try { sManager.open(!isReadOnly()); } catch (IOException ex) { if (getConfiguration().getDebug().DEBUG_STORAGE) { Debug.println("Error reading framework.info: " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } String message = NLS.bind(Msg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, ex.getMessage()); equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, message, ex); getConfiguration().setProperty(EclipseStarter.PROP_EXITCODE, "15"); //$NON-NLS-1$ String errorDialog = "" + Msg.ADAPTOR_STORAGE_INIT_FAILED_TITLE + "" + NLS.bind(Msg.ADAPTOR_STORAGE_INIT_FAILED_MSG, childRoot) + "\n" + ex.getMessage(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ getConfiguration().setProperty(EclipseStarter.PROP_EXITDATA, errorDialog); throw ex; } return sManager; } private InputStream getInfoInputStream() throws IOException { StorageManager storageManager = getChildStorageManager(); InputStream storageStream = null; try { storageStream = storageManager.getInputStream(FRAMEWORK_INFO); } catch (IOException ex) { if (getConfiguration().getDebug().DEBUG_STORAGE) { Debug.println("Error reading framework.info: " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } } finally { storageManager.close(); } if (storageStream == null && parentRoot != null) { StorageManager parentStorageManager = null; try { parentStorageManager = new StorageManager(parentRoot, LocationHelper.LOCKING_NONE, true); parentStorageManager.open(false); storageStream = parentStorageManager.getInputStream(FRAMEWORK_INFO); } catch (IOException e1) { // That's ok we will regenerate the framework.info } finally { if (parentStorageManager != null) { parentStorageManager.close(); } } } return storageStream; } }