| /******************************************************************************* |
| * Copyright (c) 2012, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Christoph Laeubrich - Bug 527175 - Storage#getSystemContent() should first make the file absolute |
| *******************************************************************************/ |
| 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.nio.charset.StandardCharsets; |
| 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.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 class StorageException extends RuntimeException { |
| private static final long serialVersionUID = 1L; |
| |
| public StorageException() { |
| super(); |
| } |
| |
| public StorageException(String message, Throwable cause) { |
| super(message, cause); |
| } |
| |
| public StorageException(String message) { |
| super(message); |
| } |
| |
| public StorageException(Throwable cause) { |
| super(cause); |
| } |
| |
| } |
| |
| public static final int VERSION = 5; |
| private static final int MR_JAR_VERSION = 4; |
| private static final int CACHED_SYSTEM_CAPS_VERION = 5; |
| 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<String> 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 { |
| String[] cachedInfo = new String[3]; |
| Storage storage = new Storage(container, cachedInfo); |
| // Do some operations that need to happen on the fully constructed Storage before returning it |
| storage.checkSystemBundle(cachedInfo); |
| 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, String[] cachedInfo) 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()), container.getConfiguration().getDebug()); |
| 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<Long, Generation> generations; |
| try { |
| generations = loadGenerations(data, cachedInfo); |
| } 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; |
| } |
| |
| public MRUBundleFileList getMRUBundleFileList() { |
| return mruList; |
| } |
| |
| 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<ModuleRevision> 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<Module> 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) { |
| equinoxContainer.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, "Error validating installed bundle.", e); //$NON-NLS-1$ |
| 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(String[] cachedInfo) { |
| 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); |
| |
| // First we must make sure the VM profile has been loaded |
| loadVMProfile(newGeneration); |
| // dealing with system bundle find the extra capabilities and exports |
| String extraCapabilities = getSystemExtraCapabilities(); |
| String extraExports = getSystemExtraPackages(); |
| |
| ModuleRevisionBuilder builder = getBuilder(newGeneration, extraCapabilities, extraExports); |
| systemModule = moduleContainer.install(null, Constants.SYSTEM_BUNDLE_LOCATION, builder, newGeneration); |
| moduleContainer.resolve(Collections.singletonList(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 { |
| // First we must make sure the VM profile has been loaded |
| loadVMProfile(currentGeneration); |
| // dealing with system bundle find the extra capabilities and exports |
| String extraCapabilities = getSystemExtraCapabilities(); |
| String extraExports = getSystemExtraPackages(); |
| File contentFile = currentGeneration.getContent(); |
| if (systemNeedsUpdate(contentFile, currentRevision, currentGeneration, extraCapabilities, extraExports, cachedInfo)) { |
| newGeneration = currentGeneration.getBundleInfo().createGeneration(); |
| newGeneration.setContent(contentFile, false); |
| ModuleRevisionBuilder newBuilder = getBuilder(newGeneration, extraCapabilities, extraExports); |
| 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<ModuleCapability> nativeEnvironments = currentRevision.getModuleCapabilities(NativeNamespace.NATIVE_NAMESPACE); |
| Map<String, Object> configMap = equinoxContainer.getConfiguration().getInitialConfig(); |
| for (ModuleCapability nativeEnvironment : nativeEnvironments) { |
| nativeEnvironment.setTransientAttrs(configMap); |
| } |
| Version frameworkVersion = null; |
| if (newGeneration != null) { |
| frameworkVersion = findFrameworkVersion(); |
| } else { |
| String sVersion = cachedInfo[0]; |
| frameworkVersion = sVersion == null ? findFrameworkVersion() : Version.parseVersion(sVersion); |
| } |
| if (frameworkVersion != null) { |
| this.equinoxContainer.getConfiguration().setConfiguration(Constants.FRAMEWORK_VERSION, frameworkVersion.toString()); |
| } |
| } 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 Version findFrameworkVersion() { |
| 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$ |
| Collection<BundleCapability> 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) { |
| return v; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private Collection<Module> refreshMRJarBundles() throws BundleException { |
| Collection<Module> 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<Module> 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.shutdownExecutors(); |
| } |
| |
| private boolean systemNeedsUpdate(File systemContent, ModuleRevision currentRevision, Generation existing, String extraCapabilities, String extraExports, String[] cachedInfo) throws BundleException { |
| if (!extraCapabilities.equals(cachedInfo[1])) { |
| return true; |
| } |
| if (!extraExports.equals(cachedInfo[2])) { |
| return true; |
| } |
| if (systemContent == null) { |
| // only do a version check in this case |
| ModuleRevisionBuilder newBuilder = getBuilder(existing, extraCapabilities, extraExports); |
| return !currentRevision.getVersion().equals(newBuilder.getVersion()); |
| } |
| if (existing.isDirectory()) { |
| systemContent = new File(systemContent, "META-INF/MANIFEST.MF"); //$NON-NLS-1$ |
| } |
| return existing.getLastModified() != secureAction.lastModified(systemContent); |
| |
| } |
| |
| 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 { |
| List<StorageHookFactory<?, ?, ?>> storageHooks = getConfiguration().getHookRegistry().getStorageHookFactories(); |
| for (StorageHookFactory<?, ?, ?> storageHook : storageHooks) { |
| URLConnection hookContent = storageHook.handleContentConnection(module, bundleLocation, in); |
| if (hookContent != null) { |
| return hookContent; |
| } |
| } |
| |
| if (in != null) { |
| return new URLConnection(null) { |
| /** |
| * @throws IOException |
| */ |
| @Override |
| public void connect() throws IOException { |
| connected = true; |
| } |
| |
| /** |
| * @throws IOException |
| */ |
| @Override |
| 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<String>() { |
| @Override |
| 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<URLConnection>() { |
| @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<StorageHookFactory<?, ?, ?>> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); |
| List<StorageHook<?, ?>> hooks = new ArrayList<>(factories.size()); |
| for (Iterator<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { |
| @SuppressWarnings("unchecked") |
| StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) iFactories.next(); |
| StorageHook<Object, Object> hook = next.createStorageHookAndValidateFactoryClass(generation); |
| if (hook != null) { |
| hooks.add(hook); |
| } |
| } |
| generation.setStorageHooks(Collections.unmodifiableList(hooks), true); |
| for (StorageHook<?, ?> hook : hooks) { |
| hook.initialize(generation.getHeaders()); |
| } |
| } |
| |
| public ModuleRevisionBuilder getBuilder(Generation generation) throws BundleException { |
| return getBuilder(generation, null, null); |
| } |
| |
| public ModuleRevisionBuilder getBuilder(Generation generation, String extraCapabilities, String extraExports) throws BundleException { |
| Dictionary<String, String> headers = generation.getHeaders(); |
| Map<String, String> mapHeaders; |
| if (headers instanceof Map) { |
| @SuppressWarnings("unchecked") |
| Map<String, String> unchecked = (Map<String, String>) headers; |
| mapHeaders = unchecked; |
| } else { |
| mapHeaders = new HashMap<>(); |
| for (Enumeration<String> 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; |
| } |
| |
| 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<String>=").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<String>=").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<String> 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 updating 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<File>() { |
| @Override |
| 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); |
| try { |
| StorageUtil.move(staged, contentFile, getConfiguration().getDebug().DEBUG_STORAGE); |
| } catch (IOException e) { |
| throw new BundleException("Error while renaming bundle file to final location: " + contentFile, //$NON-NLS-1$ |
| BundleException.READ_ERROR, e); |
| } |
| } 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$ |
| } |
| |
| /** |
| * Gets a file from storage and conditionally checks the parent storage area |
| * if the file does not exist in the child configuration. |
| * Note, this method does not check for escaping of paths from the root storage area. |
| * @param path the path relative to the root of the storage area |
| * @param checkParent if true then check the parent storage (if any) when the file |
| * does not exist in the child storage area |
| * @return the file being requested. A {@code null} value is never returned. The file |
| * returned may not exist. |
| * @throws StorageException if there was an issue getting the file |
| */ |
| public File getFile(String path, boolean checkParent) throws StorageException { |
| return getFile(null, path, checkParent); |
| } |
| |
| /** |
| * Same as {@link #getFile(String, boolean)} except takes a base parameter which is |
| * appended to the root storage area before looking for the path. If base is not |
| * null then additional checks are done to make sure the path does not escape out |
| * of the base path. |
| * @param base the additional base path to append to the root storage area. May be |
| * {@code null}, in which case no check is done for escaping out of the base path. |
| * @param path the path relative to the root + base storage area. |
| * @param checkParent if true then check the parent storage (if any) when the file |
| * does not exist in the child storage area |
| * @return the file being requested. A {@code null} value is never returned. The file |
| * returned may not exist. |
| * @throws StorageException if there was an issue getting the file |
| */ |
| public File getFile(String base, String path, boolean checkParent) throws StorageException { |
| // first check the child location |
| File childPath = getFile(childRoot, base, path); |
| // now check the parent |
| if (checkParent && parentRoot != null) { |
| if (childPath.exists()) { |
| return childPath; |
| } |
| File parentPath = getFile(parentRoot, base, 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 static File getFile(File root, String base, String path) { |
| if (base == null) { |
| // return quick; no need to check for path traversal |
| return new File(root, path); |
| } |
| |
| // if base is not null then move root to include the base |
| root = new File(root, base); |
| File result = new File(root, path); |
| |
| // do the extra check to make sure the path did not escape the root path |
| try { |
| String resultCanonical = result.getCanonicalPath(); |
| String rootCanonical = root.getCanonicalPath(); |
| if (!resultCanonical.startsWith(rootCanonical + File.separator) && !resultCanonical.equals(rootCanonical)) { |
| throw new StorageException("Invalid path: " + path); //$NON-NLS-1$ |
| } |
| } catch (IOException e) { |
| throw new StorageException("Invalid path: " + path, e); //$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| 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<File>() { |
| @Override |
| 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) { |
| return ((ReferenceInputStream) in).getReference(); |
| } |
| |
| 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[] commandComponents = ManifestElement.getArrayFromList(commandProp, " "); //$NON-NLS-1$ |
| List<String> command = new ArrayList<>(commandComponents.length + 1); |
| boolean foundFullPath = false; |
| for (String commandComponent : commandComponents) { |
| if ("[fullpath]".equals(commandComponent) || "${abspath}".equals(commandComponent)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| command.add(file.getAbsolutePath()); |
| foundFullPath = true; |
| } else { |
| command.add(commandComponent); |
| } |
| } |
| 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.<String> emptyList()); |
| } |
| |
| public BundleFile createNestedBundleFile(String nestedDir, BundleFile bundleFile, Generation generation, Collection<String> 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<BundleFileWrapperFactoryHook> 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<Void>() { |
| @Override |
| 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<Void>() { |
| @Override |
| 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<Module> modules = moduleContainer.getModules(); |
| List<Generation> 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()); |
| |
| Version curFrameworkVersion = findFrameworkVersion(); |
| out.writeUTF(curFrameworkVersion == null ? Version.emptyVersion.toString() : curFrameworkVersion.toString()); |
| |
| saveLongString(out, getSystemExtraCapabilities()); |
| saveLongString(out, getSystemExtraPackages()); |
| |
| 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<String, String> 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 saveLongString(DataOutputStream out, String value) throws IOException { |
| if (value == null) { |
| out.writeInt(0); |
| } else { |
| // don't use out.writeUTF because it has a hard string limit |
| byte[] data = value.getBytes(StandardCharsets.UTF_8); |
| out.writeInt(data.length); |
| out.write(data); |
| } |
| } |
| |
| private String readLongString(DataInputStream in) throws IOException { |
| int length = in.readInt(); |
| byte[] data = new byte[length]; |
| in.readFully(data); |
| return new String(data, StandardCharsets.UTF_8); |
| } |
| |
| private void saveStorageHookData(DataOutputStream out, List<Generation> generations) throws IOException { |
| List<StorageHookFactory<?, ?, ?>> 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<Object, Object> hook = generation.getStorageHook((Class) factory.getClass()); |
| if (hook != null) { |
| hook.save(saveContext, temp); |
| } |
| } |
| } finally { |
| temp.close(); |
| } |
| out.writeInt(tempBytes.size()); |
| out.write(tempBytes.toByteArray()); |
| } |
| } |
| |
| private Map<Long, Generation> loadGenerations(DataInputStream in, String[] cachedInfo) 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); |
| } |
| |
| cachedInfo[0] = (version >= CACHED_SYSTEM_CAPS_VERION) ? in.readUTF() : null; |
| cachedInfo[1] = (version >= CACHED_SYSTEM_CAPS_VERION) ? readLongString(in) : null; |
| cachedInfo[2] = (version >= CACHED_SYSTEM_CAPS_VERION) ? readLongString(in) : null; |
| |
| int numCachedHeaders = in.readInt(); |
| List<String> storedCachedHeaderKeys = new ArrayList<>(numCachedHeaders); |
| for (int i = 0; i < numCachedHeaders; i++) { |
| storedCachedHeaderKeys.add(ObjectPool.intern(in.readUTF())); |
| } |
| |
| int numInfos = in.readInt(); |
| Map<Long, Generation> result = new HashMap<>(numInfos); |
| List<Generation> 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<String, String> 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<Generation> generations, DataInputStream in) throws IOException { |
| List<StorageHookFactory<?, ?, ?>> factories = new ArrayList<>(getConfiguration().getHookRegistry().getStorageHookFactories()); |
| Map<Generation, List<StorageHook<?, ?>>> hookMap = new HashMap<>(); |
| int numFactories = in.readInt(); |
| for (int i = 0; i < numFactories; i++) { |
| String factoryName = in.readUTF(); |
| int version = in.readInt(); |
| StorageHookFactory<Object, Object, StorageHook<Object, Object>> factory = null; |
| for (Iterator<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { |
| @SuppressWarnings("unchecked") |
| StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) 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<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); |
| if (hook != null) { |
| 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<Object, Object> hook = factory.createStorageHookAndValidateFactoryClass(generation); |
| if (hook != null) { |
| 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<StorageHookFactory<?, ?, ?>> iFactories = factories.iterator(); iFactories.hasNext();) { |
| @SuppressWarnings("unchecked") |
| StorageHookFactory<Object, Object, StorageHook<Object, Object>> next = (StorageHookFactory<Object, Object, StorageHook<Object, Object>>) iFactories.next(); |
| // recover by reinitializing the hook |
| for (Generation generation : generations) { |
| if (generation.getBundleInfo().getBundleId() == 0) { |
| continue; // ignore system bundle |
| } |
| StorageHook<Object, Object> hook = next.createStorageHookAndValidateFactoryClass(generation); |
| if (hook != null) { |
| 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<StorageHook<?, ?>> getHooks(Map<Generation, List<StorageHook<?, ?>>> hookMap, Generation generation) { |
| List<StorageHook<?, ?>> 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)).getAbsoluteFile(); |
| if (!result.exists()) { |
| throw new IllegalStateException("Configured framework location does not exist: " + result.getAbsolutePath()); //$NON-NLS-1$ |
| } |
| return result; |
| } |
| |
| @SuppressWarnings("deprecation") |
| 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; |
| } |
| |
| @SuppressWarnings("deprecation") |
| 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<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$ |
| result.append(versionsList).append("\""); //$NON-NLS-1$ |
| result.append(",osgi.ee; osgi.ee=\"JavaSE/compact1\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ |
| result.append(versionsList).append("\""); //$NON-NLS-1$ |
| result.append(",osgi.ee; osgi.ee=\"JavaSE/compact2\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ |
| result.append(versionsList).append("\""); //$NON-NLS-1$ |
| result.append(",osgi.ee; osgi.ee=\"JavaSE/compact3\"; version:List<Version>=\"1.8"); //$NON-NLS-1$ |
| result.append(versionsList).append("\""); //$NON-NLS-1$ |
| |
| return result.toString(); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private String calculateVMPackages() { |
| try { |
| List<String> 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$ |
| Method isAutomatic = moduleDescriptorClass.getMethod("isAutomatic"); //$NON-NLS-1$ |
| Method packagesMethod = moduleDescriptorClass.getMethod("packages"); //$NON-NLS-1$ |
| Class<?> exportsClass = Class.forName("java.lang.module.ModuleDescriptor$Exports"); //$NON-NLS-1$ |
| Method isQualified = exportsClass.getMethod("isQualified"); //$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); |
| if ((Boolean) isAutomatic.invoke(descriptor)) { |
| /* |
| * Automatic modules are supposed to export all their packages. |
| * However, java.lang.module.ModuleDescriptor::exports returns an empty set for them. |
| * Add all their packages (as returned by java.lang.module.ModuleDescriptor::packages) |
| * to the list of VM supplied packages. |
| */ |
| packages.addAll((Set<String>) packagesMethod.invoke(descriptor)); |
| } else { |
| for (Object export : (Set<?>) exports.invoke(descriptor)) { |
| String pkg = (String) source.invoke(export); |
| if (!((Boolean) isQualified.invoke(export))) { |
| 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<URL> findEntries(List<Generation> generations, String path, String filePattern, int options) { |
| List<BundleFile> bundleFiles = new ArrayList<>(generations.size()); |
| for (Generation generation : generations) |
| bundleFiles.add(generation.getBundleFile()); |
| // search all the bundle files |
| List<String> 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<URL>() { |
| private int curPathIndex = 0; |
| private int curDataIndex = 0; |
| private URL nextElement = null; |
| |
| @Override |
| public boolean hasMoreElements() { |
| if (nextElement != null) |
| return true; |
| getNextElement(); |
| return nextElement != null; |
| } |
| |
| @Override |
| 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<String> listEntryPaths(List<BundleFile> bundleFiles, String path, String filePattern, int options) { |
| // Use LinkedHashSet for optimized performance of contains() plus |
| // ordering guarantees. |
| LinkedHashSet<String> pathList = new LinkedHashSet<>(); |
| Filter patternFilter = null; |
| Hashtable<String, String> 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 { |
| StringBuilder 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 StringBuilder(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<String> listEntryPaths(BundleFile bundleFile, String path, Filter patternFilter, Hashtable<String, String> patternProps, int options, LinkedHashSet<String> pathList) { |
| if (pathList == null) |
| pathList = new LinkedHashSet<>(); |
| Enumeration<String> 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 = "<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$ |
| 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; |
| } |
| } |