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
/******************************************************************************* * 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.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 –
/** * @throws IOException */
@Override public void connect() throws IOException { connected = true; }
Throws:
  • IOException –
/** * @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); if (!StorageUtil.move(staged, contentFile, getConfiguration().getDebug().DEBUG_STORAGE)) { throw new BundleException("Error while renaming bundle file to final location: " + contentFile); //$NON-NLS-1$ } } else { contentFile = staged; } return contentFile; } private static String getBundleFilePath(long bundleID, long generationID) { return bundleID + "/" + generationID + "/" + BUNDLE_FILE_NAME; //$NON-NLS-1$ //$NON-NLS-2$ }
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.
Params:
  • path – the path relative to the root of the storage area
  • checkParent – if true then check the parent storage (if any) when the file does not exist in the child storage area
Throws:
Returns:the file being requested. A null value is never returned. The file returned may not exist.
/** * 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 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.
Params:
  • base – the additional base path to append to the root storage area. May be null, in which case no check is done for escaping out of the base path.
  • path – the path relative to the root + base storage area.
  • checkParent – if true then check the parent storage (if any) when the file does not exist in the child storage area
Throws:
Returns:the file being requested. A null value is never returned. The file returned may not exist.
/** * 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.
Params:
  • file – the file to set the permissions on
/** * 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("UTF-8"); //$NON-NLS-1$ 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, "UTF-8"); //$NON-NLS-1$ } 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.
Params:
  • bundleFiles – the list of bundle files to search in
  • path – The path name in which to look.
  • filePattern – The file name pattern for selecting resource names in the specified path.
  • options – The options for listing resource names.
See Also:
Returns:a list of resource names. If no resources are found then the empty list is returned.
/** * 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; } }