/******************************************************************************* * Copyright (c) 2005, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Rob Harrop - SpringSource Inc. (bug 247520 and 253942) *******************************************************************************/ package org.eclipse.osgi.internal.baseadaptor; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.*; import java.util.*; import org.eclipse.core.runtime.adaptor.EclipseStarter; import org.eclipse.core.runtime.adaptor.LocationManager; import org.eclipse.core.runtime.internal.adaptor.EclipseAdaptorMsg; import org.eclipse.osgi.baseadaptor.BaseAdaptor; import org.eclipse.osgi.baseadaptor.BaseData; import org.eclipse.osgi.baseadaptor.bundlefile.*; import org.eclipse.osgi.baseadaptor.hooks.*; import org.eclipse.osgi.framework.adaptor.*; import org.eclipse.osgi.framework.debug.Debug; import org.eclipse.osgi.framework.debug.FrameworkDebugOptions; import org.eclipse.osgi.framework.internal.core.*; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.framework.util.KeyedHashSet; import org.eclipse.osgi.internal.loader.BundleLoader; import org.eclipse.osgi.internal.loader.BundleLoaderProxy; import org.eclipse.osgi.service.datalocation.Location; import org.eclipse.osgi.service.resolver.*; 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.*; public class BaseStorage implements SynchronousBundleListener { private static final String RUNTIME_ADAPTOR = FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + "/eclipseadaptor"; //$NON-NLS-1$ private static final String OPTION_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/debug/platformadmin"; //$NON-NLS-1$ private static final String OPTION_PLATFORM_ADMIN_RESOLVER = RUNTIME_ADAPTOR + "/debug/platformadmin/resolver"; //$NON-NLS-1$ private static final String OPTION_MONITOR_PLATFORM_ADMIN = RUNTIME_ADAPTOR + "/resolver/timing"; //$NON-NLS-1$ private static final String OPTION_RESOLVER_READER = RUNTIME_ADAPTOR + "/resolver/reader/timing"; //$NON-NLS-1$ private static final String PROP_FRAMEWORK_EXTENSIONS = "osgi.framework.extensions"; //$NON-NLS-1$ private static final String PROP_BUNDLE_STORE = "osgi.bundlestore"; //$NON-NLS-1$ // The name of the bundle data directory static final String DATA_DIR_NAME = "data"; //$NON-NLS-1$ static final String LIB_TEMP = "libtemp"; //$NON-NLS-1$ // System property used to determine whether State saver needs to be enabled private static final String PROP_ENABLE_STATE_SAVER = "eclipse.enableStateSaver"; //$NON-NLS-1$ static final String BUNDLEFILE_NAME = "bundlefile"; //$NON-NLS-1$ // System property used to clean the osgi configuration area private static final String PROP_CLEAN = "osgi.clean"; //$NON-NLS-1$ /** The current bundle data version */ public static final byte BUNDLEDATA_VERSION = 18; /** * flag to indicate a framework extension is being intialized */ public static final byte EXTENSION_INITIALIZE = 0x01; /** * flag to indicate a framework extension is being installed */ public static final byte EXTENSION_INSTALLED = 0x02; /** * flag to indicate a framework extension is being uninstalled */ public static final byte EXTENSION_UNINSTALLED = 0x04; /** * flag to indicate a framework extension is being updated */ public static final byte EXTENSION_UPDATED = 0x08; /** The BundleData is for a bundle exploded in a directory */ public static final int TYPE_DIRECTORYBUNDLE = 0x10000000; /** The BundleData is for a bundle contained in a file (typically jar) */ public static final int TYPE_FILEBUNDLE = 0x20000000; /** * the file name for the delete flag. If this file exists in one a directory * under the bundle store area then it will be removed during the * compact operation. */ public static final String DELETE_FLAG = ".delete"; //$NON-NLS-1$ private static final String PERM_DATA_FILE = ".permdata"; //$NON-NLS-1$ private static final byte PERMDATA_VERSION = 1; private MRUBundleFileList mruList = new MRUBundleFileList(); BaseAdaptor adaptor; // assume a file: installURL private String installPath; private StorageManager storageManager; private StateManager stateManager; // no need to synchronize on storageHooks because the elements are statically set in initialize private KeyedHashSet storageHooks = new KeyedHashSet(5, false); private BundleContext context; private SynchronousBundleListener extensionListener; /** * The add URL method used to support framework extensions */ private final Method addFwkURLMethod; private final Method addExtURLMethod; /** * The list of configured framework extensions */ private String[] configuredExtensions; private long timeStamp = 0; private int initialBundleStartLevel = 1; private final Object nextIdMonitor = new Object(); private volatile long nextId = 1; /** * directory containing installed bundles */ private File bundleStoreRoot; private BasePermissionStorage permissionStorage; private StateSaver stateSaver; private boolean invalidState; private boolean storageManagerClosed; BaseStorage() { // make constructor package private // initialize the addXYZURLMethods to support framework extensions addFwkURLMethod = findAddURLMethod(getFwkClassLoader(), "addURL"); //$NON-NLS-1$ addExtURLMethod = findAddURLMethod(getExtClassLoader(), "addURL"); //$NON-NLS-1$ } public void initialize(BaseAdaptor initAdaptor) throws IOException { this.adaptor = initAdaptor; setDebugOptions(); if (Boolean.valueOf(FrameworkProperties.getProperty(BaseStorage.PROP_CLEAN)).booleanValue()) cleanOSGiCache(); // we need to set the install path as soon as possible so we can determine // the absolute location of install relative URLs Location installLoc = LocationManager.getInstallLocation(); if (installLoc != null) { URL installURL = installLoc.getURL(); // assume install URL is file: based installPath = installURL.getPath(); } boolean readOnlyConfiguration = LocationManager.getConfigurationLocation().isReadOnly(); storageManager = initFileManager(LocationManager.getOSGiConfigurationDir(), readOnlyConfiguration ? "none" : null, readOnlyConfiguration); //$NON-NLS-1$ storageManagerClosed = false; // initialize the storageHooks StorageHook[] hooks = initAdaptor.getHookRegistry().getStorageHooks(); for (int i = 0; i < hooks.length; i++) storageHooks.add(hooks[i]); } private static Method findAddURLMethod(ClassLoader cl, String name) { if (cl == null) return null; return findMethod(cl.getClass(), name, new Class[] {URL.class}); } // recursively searches a class and it's superclasses for a (potentially inaccessable) method private static Method findMethod(Class<?> clazz, String name, Class<?>[] args) { if (clazz == null) return null; // ends the recursion when getSuperClass returns null try { Method result = clazz.getDeclaredMethod(name, args); result.setAccessible(true); return result; } catch (NoSuchMethodException e) { // do nothing look in super class below } catch (SecurityException e) { // if we do not have the permissions then we will not find the method } return findMethod(clazz.getSuperclass(), name, args); } private static void callAddURLMethod(ClassLoader cl, Method meth, URL arg) throws InvocationTargetException { try { meth.invoke(cl, new Object[] {arg}); } catch (Throwable t) { throw new InvocationTargetException(t); } } private ClassLoader getFwkClassLoader() { return this.getClass().getClassLoader(); } private ClassLoader getExtClassLoader() { ClassLoader cl = ClassLoader.getSystemClassLoader(); ClassLoader extcl = cl.getParent(); while ((extcl != null) && (extcl.getParent() != null)) { extcl = extcl.getParent(); } return extcl; } private static void setDebugOptions() { FrameworkDebugOptions options = FrameworkDebugOptions.getDefault(); // may be null if debugging is not enabled if (options == null) return; StateManager.DEBUG = options != null; StateManager.DEBUG_READER = options.getBooleanOption(OPTION_RESOLVER_READER, false); StateManager.MONITOR_PLATFORM_ADMIN = options.getBooleanOption(OPTION_MONITOR_PLATFORM_ADMIN, false); StateManager.DEBUG_PLATFORM_ADMIN = options.getBooleanOption(OPTION_PLATFORM_ADMIN, false); StateManager.DEBUG_PLATFORM_ADMIN_RESOLVER = options.getBooleanOption(OPTION_PLATFORM_ADMIN_RESOLVER, false); } protected StorageManager initFileManager(File baseDir, String lockMode, boolean readOnly) throws IOException { StorageManager sManager = new StorageManager(baseDir, lockMode, readOnly); try { sManager.open(!readOnly); } catch (IOException ex) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading framework metadata: " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, ex.getMessage()); FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, ex, null); adaptor.getFrameworkLog().log(logEntry); FrameworkProperties.setProperty(EclipseStarter.PROP_EXITCODE, "15"); //$NON-NLS-1$ String errorDialog = "<title>" + AdaptorMsg.ADAPTOR_STORAGE_INIT_FAILED_TITLE + "</title>" + NLS.bind(AdaptorMsg.ADAPTOR_STORAGE_INIT_FAILED_MSG, baseDir) + "\n" + ex.getMessage(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ FrameworkProperties.setProperty(EclipseStarter.PROP_EXITDATA, errorDialog); throw ex; } return sManager; } public boolean isReadOnly() { return storageManager.isReadOnly(); } public void compact() { if (!isReadOnly()) compact(getBundleStoreRoot()); } private void compact(File directory) { if (Debug.DEBUG_GENERAL) 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 (BaseStorage.DATA_DIR_NAME.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, BaseStorage.DELETE_FLAG); // and the directory is marked for delete if (delete.exists()) { // if rm fails to delete the directory and .delete was removed if (!AdaptorUtil.rm(target) && !delete.exists()) { try { // recreate .delete FileOutputStream out = new FileOutputStream(delete); out.close(); } catch (IOException e) { if (Debug.DEBUG_GENERAL) Debug.println("Unable to write " + delete.getPath() + ": " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } } } else { compact(target); /* descend into directory */ } } } /** * @throws IOException */ public long getFreeSpace() throws IOException { // cannot implement this without native code! return -1; } public File getDataFile(BaseData data, String path) { BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); if (storageHook == null) return null; return storageHook.getDataFile(path); } BaseAdaptor getAdaptor() { return adaptor; } public void installNativeCode(BaseData data, String[] nativepaths) throws BundleException { if (nativepaths.length > 0) { BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); if (storageHook != null) storageHook.installNativePaths(nativepaths); } } public Dictionary<String, String> loadManifest(BaseData data) throws BundleException { return loadManifest(data, false); } public Dictionary<String, String> loadManifest(BaseData bundleData, boolean firstTime) throws BundleException { Dictionary<String, String> result = null; StorageHook[] dataStorageHooks = bundleData.getStorageHooks(); for (int i = 0; i < dataStorageHooks.length && result == null; i++) result = dataStorageHooks[i].getManifest(firstTime); if (result == null) result = AdaptorUtil.loadManifestFrom(bundleData); if (result == null) throw new BundleException(NLS.bind(AdaptorMsg.MANIFEST_NOT_FOUND_EXCEPTION, Constants.OSGI_BUNDLE_MANIFEST, bundleData.getLocation()), BundleException.MANIFEST_ERROR); return result; } public File getExtractFile(BaseData data, String path) { BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); if (storageHook == null) return null; // first check the child generation dir File childGenDir = storageHook.getGenerationDir(); if (childGenDir != null) { File childPath = new File(childGenDir, path); if (childPath.exists()) return childPath; } // now check the parent File parentGenDir = storageHook.getParentGenerationDir(); if (parentGenDir != null) { // there is a parent generation check if the file exists File parentPath = new File(parentGenDir, path); if (parentPath.exists()) // only use the parent generation file if it exists; do not extract there return parentPath; } // did not exist in both locations; create a file for extraction. File bundleGenerationDir = storageHook.createGenerationDir(); /* if the generation dir exists, then we have place to cache */ if (bundleGenerationDir != null && bundleGenerationDir.exists()) return new File(bundleGenerationDir, path); return null; } public BaseData[] getInstalledBundles() { try { return readBundleDatas(); } catch (Throwable t) { // be safe here and throw out the results and start over // otherwise this would result in a failed launch FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, "Error loading bundle datas. Recalculating cache.", 0, t, null); //$NON-NLS-1$ adaptor.getFrameworkLog().log(logEntry); return null; } } private BaseData[] readBundleDatas() { InputStream bundleDataStream = findStorageStream(LocationManager.BUNDLE_DATA_FILE); if (bundleDataStream == null) return null; try { DataInputStream in = new DataInputStream(new BufferedInputStream(bundleDataStream)); try { byte version = in.readByte(); if (version != BUNDLEDATA_VERSION) return null; timeStamp = in.readLong(); initialBundleStartLevel = in.readInt(); nextId = in.readLong(); int numStorageHooks = in.readInt(); StorageHook[] hooks = adaptor.getHookRegistry().getStorageHooks(); if (numStorageHooks != hooks.length) return null; // must have the same number of storagehooks to properly read the data for (int i = 0; i < numStorageHooks; i++) { Object storageKey = hooks[i].getKey(); int storageVersion = hooks[i].getStorageVersion(); if (!storageKey.equals(in.readUTF()) || storageVersion != in.readInt()) return null; // some storage hooks have changed must throw the data away. } int bundleCount = in.readInt(); List<BaseData> result = new ArrayList<BaseData>(bundleCount); long id = -1; boolean bundleDiscarded = false; for (int i = 0; i < bundleCount; i++) { boolean error = false; BaseData data = null; try { id = in.readLong(); if (id != 0) { data = loadBaseData(id, in); data.getBundleFile(); StorageHook[] dataStorageHooks = data.getStorageHooks(); for (int j = 0; j < dataStorageHooks.length; j++) dataStorageHooks[j].validate(); if (Debug.DEBUG_GENERAL) Debug.println("BundleData created: " + data); //$NON-NLS-1$ processExtension(data, EXTENSION_INITIALIZE); result.add(data); } } catch (IllegalArgumentException e) { // may be from data.getBundleFile() bundleDiscarded = true; error = true; } catch (BundleException e) { // should never happen bundleDiscarded = true; error = true; } catch (IOException e) { bundleDiscarded = true; error = true; if (Debug.DEBUG_GENERAL) { Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(e); } } if (error && data != null) { BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); storageHook.delete(true, BaseStorageHook.DEL_BUNDLE_STORE); } } if (bundleDiscarded) FrameworkProperties.setProperty(EclipseStarter.PROP_REFRESH_BUNDLES, "true"); //$NON-NLS-1$ return result.toArray(new BaseData[result.size()]); } finally { in.close(); } } catch (IOException e) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading framework metadata: " + e.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(e); } } return null; } void saveAllData(boolean shutdown) { if (Debug.DEBUG_GENERAL) Debug.println("Saving framework data ..."); //$NON-NLS-1$ if (storageManagerClosed) try { storageManager.open(!LocationManager.getConfigurationLocation().isReadOnly()); storageManagerClosed = false; } catch (IOException e) { String message = NLS.bind(EclipseAdaptorMsg.ECLIPSE_STARTUP_FILEMANAGER_OPEN_ERROR, e.getMessage()); FrameworkLogEntry logEntry = new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, message, 0, e, null); adaptor.getFrameworkLog().log(logEntry); } saveBundleDatas(); saveStateData(shutdown); savePermissionStorage(); if (shutdown) stateManager.stopDataManager(); } private BasePermissionStorage readPermissionData() { BasePermissionStorage result = new BasePermissionStorage(this); InputStream permDataStream = findStorageStream(PERM_DATA_FILE); if (permDataStream == null) return result; try { DataInputStream in = new DataInputStream(new BufferedInputStream(permDataStream)); try { if (PERMDATA_VERSION != in.readByte()) return result; // read the default permissions first int numPerms = in.readInt(); if (numPerms > 0) { String[] perms = new String[numPerms]; for (int i = 0; i < numPerms; i++) perms[i] = in.readUTF(); result.setPermissionData(null, perms); } int numLocs = in.readInt(); if (numLocs > 0) for (int i = 0; i < numLocs; i++) { String loc = in.readUTF(); numPerms = in.readInt(); String[] perms = new String[numPerms]; for (int j = 0; j < numPerms; j++) perms[j] = in.readUTF(); result.setPermissionData(loc, perms); } int numCondPerms = in.readInt(); if (numCondPerms > 0) { String[] condPerms = new String[numCondPerms]; for (int i = 0; i < numCondPerms; i++) condPerms[i] = in.readUTF(); result.saveConditionalPermissionInfos(condPerms); } result.setDirty(false); } finally { in.close(); } } catch (IOException e) { adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e)); } return result; } private void savePermissionStorage() { if (permissionStorage == null || isReadOnly() || !permissionStorage.isDirty()) return; if (Debug.DEBUG_GENERAL) Debug.println("About to save permission data ..."); //$NON-NLS-1$ try { ManagedOutputStream fmos = storageManager.getOutputStream(PERM_DATA_FILE); DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fmos)); boolean error = true; try { out.writeByte(PERMDATA_VERSION); // always write the default permissions first String[] defaultPerms = permissionStorage.getPermissionData(null); out.writeInt(defaultPerms == null ? 0 : defaultPerms.length); if (defaultPerms != null) for (int i = 0; i < defaultPerms.length; i++) out.writeUTF(defaultPerms[i]); String[] locations = permissionStorage.getLocations(); out.writeInt(locations == null ? 0 : locations.length); if (locations != null) for (int i = 0; i < locations.length; i++) { out.writeUTF(locations[i]); String[] perms = permissionStorage.getPermissionData(locations[i]); out.writeInt(perms == null ? 0 : perms.length); if (perms != null) for (int j = 0; j < perms.length; j++) out.writeUTF(perms[j]); } String[] condPerms = permissionStorage.getConditionalPermissionInfos(); out.writeInt(condPerms == null ? 0 : condPerms.length); if (condPerms != null) for (int i = 0; i < condPerms.length; i++) out.writeUTF(condPerms[i]); out.close(); permissionStorage.setDirty(false); error = false; } finally { // if something happens, don't close a corrupt file if (error) { fmos.abort(); try { out.close(); } catch (IOException e) {/*ignore*/ } } } } catch (IOException e) { adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e)); return; } } private void saveBundleDatas() { // the cache and the state match if (stateManager == null || isReadOnly() || (timeStamp == stateManager.getSystemState().getTimeStamp() && !stateManager.saveNeeded())) return; if (Debug.DEBUG_GENERAL) Debug.println("Saving bundle data ..."); //$NON-NLS-1$ try { ManagedOutputStream fmos = storageManager.getOutputStream(LocationManager.BUNDLE_DATA_FILE); DataOutputStream out = new DataOutputStream(new BufferedOutputStream(fmos)); boolean error = true; try { out.writeByte(BUNDLEDATA_VERSION); out.writeLong(stateManager.getSystemState().getTimeStamp()); out.writeInt(initialBundleStartLevel); out.writeLong(nextId); StorageHook[] hooks = adaptor.getHookRegistry().getStorageHooks(); out.writeInt(hooks.length); for (int i = 0; i < hooks.length; i++) { out.writeUTF((String) hooks[i].getKey()); out.writeInt(hooks[i].getStorageVersion()); } Bundle[] bundles = context.getBundles(); out.writeInt(bundles.length); for (int i = 0; i < bundles.length; i++) { long id = bundles[i].getBundleId(); out.writeLong(id); if (id != 0) { BundleData data = ((org.eclipse.osgi.framework.internal.core.AbstractBundle) bundles[i]).getBundleData(); saveBaseData((BaseData) data, out); } } out.close(); // update the 'timeStamp' after the changed Meta data is saved. timeStamp = stateManager.getSystemState().getTimeStamp(); error = false; } finally { // if something happens, don't close a corrupt file if (error) { fmos.abort(); try { out.close(); } catch (IOException e) {/*ignore*/ } } } } catch (IOException e) { adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e)); return; } } private void cleanRemovalPendings(State systemState, BundleDescription[] removalPendings) { if (removalPendings.length == 0) return; systemState.resolve(removalPendings); for (int i = 0; i < removalPendings.length; i++) { Object userObject = removalPendings[i].getUserObject(); if (userObject instanceof BundleLoaderProxy) { BundleLoader.closeBundleLoader((BundleLoaderProxy) userObject); try { ((BundleLoaderProxy) userObject).getBundleData().close(); } catch (IOException e) { // ignore } } else if (userObject instanceof BundleData) { try { ((BundleData) userObject).close(); } catch (IOException e) { // ignore } } } } private void saveStateData(boolean shutdown) { State systemState = stateManager.getSystemState(); if (shutdown && "true".equals(FrameworkProperties.getProperty("osgi.forcedRestart"))) //$NON-NLS-1$ //$NON-NLS-2$ // increment the state timestamp if a forced restart happened. systemState.setTimeStamp(systemState.getTimeStamp() + 1); BundleDescription[] removalPendings = systemState.getRemovalPending(); if (removalPendings.length > 0) { if (!shutdown) return; // never save if removal pending; unless we are shutting down cleanRemovalPendings(systemState, removalPendings); } if (stateManager == null || isReadOnly() || !stateManager.saveNeeded()) return; if (Debug.DEBUG_GENERAL) Debug.println("Saving resolver state data ..."); //$NON-NLS-1$ File stateTmpFile = null; File lazyTmpFile = null; try { stateTmpFile = File.createTempFile(LocationManager.STATE_FILE, ".new", LocationManager.getOSGiConfigurationDir()); //$NON-NLS-1$ lazyTmpFile = File.createTempFile(LocationManager.LAZY_FILE, ".new", LocationManager.getOSGiConfigurationDir()); //$NON-NLS-1$ if (shutdown) stateManager.shutdown(stateTmpFile, lazyTmpFile); else synchronized (stateManager) { stateManager.update(stateTmpFile, lazyTmpFile); } storageManager.lookup(LocationManager.STATE_FILE, true); storageManager.lookup(LocationManager.LAZY_FILE, true); storageManager.update(new String[] {LocationManager.STATE_FILE, LocationManager.LAZY_FILE}, new String[] {stateTmpFile.getName(), lazyTmpFile.getName()}); } catch (IOException e) { adaptor.getFrameworkLog().log(new FrameworkEvent(FrameworkEvent.ERROR, context.getBundle(), e)); } finally { if (stateTmpFile != null && stateTmpFile.exists()) stateTmpFile.delete(); if (lazyTmpFile != null && lazyTmpFile.exists()) lazyTmpFile.delete(); } } public PermissionStorage getPermissionStorage() { if (permissionStorage == null) permissionStorage = readPermissionData(); return permissionStorage; } public int getInitialBundleStartLevel() { return initialBundleStartLevel; } public void setInitialBundleStartLevel(int value) { this.initialBundleStartLevel = value; requestSave(); } public void save(BaseData data) { if (data.isDirty()) { timeStamp--; // Change the value of the timeStamp, as a marker that something changed. requestSave(); data.setDirty(false); } } public BundleOperation installBundle(String location, URLConnection source) { BaseData data = createBaseData(getNextBundleId(), location); return new BundleInstall(data, source, this); } public BundleOperation updateBundle(BaseData data, URLConnection source) { return new BundleUpdate(data, source, this); } public BundleOperation uninstallBundle(BaseData data) { return new BundleUninstall(data, this); } protected Object getBundleContent(BaseData bundledata) { BaseStorageHook storageHook = (BaseStorageHook) bundledata.getStorageHook(BaseStorageHook.KEY); if (storageHook == null) throw new IllegalStateException(); return storageHook.isReference() ? new File(storageHook.getFileName()) : new File(storageHook.getGenerationDir(), storageHook.getFileName()); } public BundleFile createBundleFile(Object content, BaseData data) throws IOException { boolean base = false; if (content == null) { // this must be a request for the base bundlefile base = true; // get the content of this bundle content = getBundleContent(data); } BundleFile result = data.getBundleFile(content, base); if (result != null) return result; // Ask factories before doing the default behavior BundleFileFactoryHook[] factories = adaptor.getHookRegistry().getBundleFileFactoryHooks(); for (int i = 0; i < factories.length && result == null; i++) result = factories[i].createBundleFile(content, data, base); // No factories configured or they declined to create the bundle file; do default if (result == null && content instanceof File) { File file = (File) content; if (isDirectory(data, base, file)) result = new DirBundleFile(file); else result = new ZipBundleFile(file, data, getMRUList()); } if (result == null && content instanceof String) { // here we assume the content is a path offset into the base bundle file; create a NestedDirBundleFile result = new NestedDirBundleFile(data.getBundleFile(), (String) content); } if (result == null) // nothing we can do; must throw exception for the content throw new IOException("Cannot create bundle file for content of type: " + content.getClass().getName()); //$NON-NLS-1$ // try creating a wrapper bundlefile out of it. BundleFileWrapperFactoryHook[] wrapperFactories = adaptor.getHookRegistry().getBundleFileWrapperFactoryHooks(); BundleFileWrapperChain wrapped = wrapperFactories.length == 0 ? null : new BundleFileWrapperChain(result, null); for (int i = 0; i < wrapperFactories.length; i++) { BundleFile wrapperBundle = wrapperFactories[i].wrapBundleFile(result, content, data, base); if (wrapperBundle != null && wrapperBundle != result) result = wrapped = new BundleFileWrapperChain(wrapperBundle, wrapped); } if (!base) data.setBundleFile(content, result); return result; } private synchronized MRUBundleFileList getMRUList() { if (mruList == null) mruList = new MRUBundleFileList(); return mruList; } private boolean isDirectory(BaseData data, boolean base, File file) { if (!base) // there is no other place to check this; just consult the file directly return file.isDirectory(); boolean isDirectory = false; // first check if the type bits have been set int type = data.getType(); if ((type & (TYPE_DIRECTORYBUNDLE | TYPE_FILEBUNDLE)) == 0) { // no type bits set; consult the file and save the result isDirectory = file.isDirectory(); data.setType(type | (isDirectory ? TYPE_DIRECTORYBUNDLE : TYPE_FILEBUNDLE)); } else { // type bits have been set check to see if this is a directory bundle. isDirectory = (type & TYPE_DIRECTORYBUNDLE) != 0; } return isDirectory; } public synchronized StateManager getStateManager() { if (stateManager != null) return stateManager; stateManager = readStateData(); checkSystemState(stateManager.getSystemState()); return stateManager; } private void checkSystemState(State state) { BundleDescription[] bundles = state.getBundles(); if (bundles == null) return; boolean removedBundle = false; for (int i = 0; i < bundles.length; i++) { if (adaptor.getBundle(bundles[i].getBundleId()) == null) { state.removeBundle(bundles[i]); removedBundle = true; } } if (removedBundle) state.resolve(false); // do a full resolve BundleDescription systemBundle = state.getBundle(0); if (systemBundle == null || !systemBundle.isResolved()) { ResolverError[] errors = systemBundle == null ? new ResolverError[0] : state.getResolverErrors(systemBundle); StringBuffer sb = new StringBuffer(); for (int i = 0; i < errors.length; i++) { sb.append(errors[i].toString()); if (i < errors.length - 1) sb.append(", "); //$NON-NLS-1$ } // this would be a bug in the framework throw new IllegalStateException(NLS.bind(AdaptorMsg.SYSTEMBUNDLE_NOTRESOLVED, sb.toString())); } } private StateManager readStateData() { File[] stateFiles = findStorageFiles(new String[] {LocationManager.STATE_FILE, LocationManager.LAZY_FILE}); File stateFile = stateFiles[0]; File lazyFile = stateFiles[1]; stateManager = new StateManager(stateFile, lazyFile, context, timeStamp); State systemState = null; if (!invalidState) { systemState = stateManager.readSystemState(); if (systemState != null) return stateManager; } systemState = stateManager.createSystemState(); Bundle[] installedBundles = context.getBundles(); if (installedBundles == null) return stateManager; StateObjectFactory factory = stateManager.getFactory(); for (int i = 0; i < installedBundles.length; i++) { AbstractBundle toAdd = (AbstractBundle) installedBundles[i]; try { // make sure we get the real manifest as if this is the first time. Dictionary<String, String> toAddManifest = loadManifest((BaseData) toAdd.getBundleData(), true); BundleDescription newDescription = factory.createBundleDescription(systemState, toAddManifest, toAdd.getLocation(), toAdd.getBundleId()); systemState.addBundle(newDescription); } catch (BundleException be) { // just ignore bundle datas with invalid manifests } } // we do not set the cached timestamp here because we want a new one to be used from the new system state object (bug 132978) // we need the state resolved systemState.resolve(); invalidState = false; return stateManager; } private File[] findStorageFiles(String[] fileNames) { File[] storageFiles = new File[fileNames.length]; try { for (int i = 0; i < storageFiles.length; i++) storageFiles[i] = storageManager.lookup(fileNames[i], false); } catch (IOException ex) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } } boolean success = true; for (int i = 0; i < storageFiles.length; i++) if (storageFiles[i] == null || !storageFiles[i].isFile()) { success = false; break; } if (success) return storageFiles; //if it does not exist, try to read it from the parent Location parentConfiguration = null; Location currentConfiguration = LocationManager.getConfigurationLocation(); if (currentConfiguration != null && (parentConfiguration = currentConfiguration.getParentLocation()) != null) { try { File stateLocationDir = new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME); StorageManager newFileManager = initFileManager(stateLocationDir, "none", true); //$NON-NLS-1$); for (int i = 0; i < storageFiles.length; i++) storageFiles[i] = newFileManager.lookup(fileNames[i], false); newFileManager.close(); } catch (IOException ex) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } } } else { try { //it did not exist in either place, so create it in the original location if (!isReadOnly()) { for (int i = 0; i < storageFiles.length; i++) storageFiles[i] = storageManager.lookup(fileNames[i], true); } } catch (IOException ex) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading state file " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } } } return storageFiles; } public void frameworkStart(BundleContext fwContext) { this.context = fwContext; // System property can be set to enable state saver or not. if (Boolean.valueOf(FrameworkProperties.getProperty(BaseStorage.PROP_ENABLE_STATE_SAVER, "true")).booleanValue()) //$NON-NLS-1$ stateSaver = new StateSaver(); } public void frameworkStop(BundleContext fwContext) { if (stateSaver != null) stateSaver.shutdown(); saveAllData(true); storageManager.close(); storageManagerClosed = true; if (extensionListener != null) context.removeBundleListener(extensionListener); MRUBundleFileList current = mruList; if (current != null) current.shutdown(); mruList = null; stateManager = null; } public void frameworkStopping(BundleContext fwContext) { // do nothing in storage } public void addProperties(Properties properties) { // set the extension support if we found the addURL method if (addFwkURLMethod != null) properties.put(Constants.SUPPORTS_FRAMEWORK_EXTENSION, "true"); //$NON-NLS-1$ // store bundleStore back into adaptor properties for others to see properties.put(BaseStorage.PROP_BUNDLE_STORE, getBundleStoreRoot().getAbsolutePath()); } private InputStream findStorageStream(String fileName) { InputStream storageStream = null; try { storageStream = storageManager.getInputStream(fileName); } catch (IOException ex) { if (Debug.DEBUG_GENERAL) { Debug.println("Error reading framework metadata: " + ex.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ex); } } if (storageStream == null) { Location currentConfiguration = LocationManager.getConfigurationLocation(); Location parentConfiguration = null; if (currentConfiguration != null && (parentConfiguration = currentConfiguration.getParentLocation()) != null) { try { File bundledataLocationDir = new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME); StorageManager newStorageManager = initFileManager(bundledataLocationDir, "none", true); //$NON-NLS-1$ storageStream = newStorageManager.getInputStream(fileName); newStorageManager.close(); } catch (MalformedURLException e1) { // This will not happen since all the URLs are derived by us // and we are GODS! } catch (IOException e1) { // That's ok we will regenerate the .bundleData } } } return storageStream; } protected void saveBaseData(BaseData bundledata, DataOutputStream out) throws IOException { StorageHook[] hooks = bundledata.getStorageHooks(); out.writeInt(hooks.length); for (int i = 0; i < hooks.length; i++) { out.writeUTF((String) hooks[i].getKey()); hooks[i].save(out); } } protected BaseData loadBaseData(long id, DataInputStream in) throws IOException { BaseData result = new BaseData(id, adaptor); int numHooks = in.readInt(); StorageHook[] hooks = new StorageHook[numHooks]; for (int i = 0; i < numHooks; i++) { String hookKey = in.readUTF(); StorageHook storageHook = (StorageHook) storageHooks.getByKey(hookKey); if (storageHook == null) throw new IOException(); hooks[i] = storageHook.load(result, in); } result.setStorageHooks(hooks); return result; } protected BaseData createBaseData(long id, String location) { BaseData result = new BaseData(id, adaptor); result.setLocation(location); return result; } public String getInstallPath() { return installPath; } private void cleanOSGiCache() { File osgiConfig = LocationManager.getOSGiConfigurationDir(); if (!AdaptorUtil.rm(osgiConfig)) adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, "The -clean (osgi.clean) option was not successful. Unable to clean the storage area: " + osgiConfig.getAbsolutePath(), 0, null, null)); //$NON-NLS-1$ } /** * Processes an extension bundle * @param bundleData the extension bundle data * @param type the type of extension bundle * @throws BundleException on any errors or if the extension bundle type is not supported */ protected void processExtension(BaseData bundleData, byte type) throws BundleException { if ((bundleData.getType() & BundleData.TYPE_FRAMEWORK_EXTENSION) != 0) { validateExtension(bundleData); processFrameworkExtension(bundleData, type); } else if ((bundleData.getType() & BundleData.TYPE_BOOTCLASSPATH_EXTENSION) != 0) { validateExtension(bundleData); processBootExtension(bundleData, type); } else if ((bundleData.getType() & BundleData.TYPE_EXTCLASSPATH_EXTENSION) != 0) { validateExtension(bundleData); processExtExtension(bundleData, type); } } /** * Validates the extension bundle metadata * @param bundleData the extension bundle data * @throws BundleException if the extension bundle metadata is invalid */ private void validateExtension(BundleData bundleData) throws BundleException { Dictionary<String, String> extensionManifest = bundleData.getManifest(); if (extensionManifest.get(Constants.IMPORT_PACKAGE) != null) throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_IMPORT_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR); if (extensionManifest.get(Constants.REQUIRE_BUNDLE) != null) throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_REQUIRE_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR); if (extensionManifest.get(Constants.BUNDLE_NATIVECODE) != null) throw new BundleException(NLS.bind(AdaptorMsg.ADAPTOR_EXTENSION_NATIVECODE_ERROR, bundleData.getLocation()), BundleException.MANIFEST_ERROR); } /** * Processes a framework extension bundle * @param bundleData the extension bundle data * @param type the type of extension bundle * @throws BundleException on errors or if framework extensions are not supported */ protected void processFrameworkExtension(BaseData bundleData, byte type) throws BundleException { if (addFwkURLMethod == null) throw new BundleException("Framework extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$ addExtensionContent(bundleData, type, getFwkClassLoader(), addFwkURLMethod); } protected void processExtExtension(BaseData bundleData, byte type) throws BundleException { if (addExtURLMethod == null) throw new BundleException("Extension classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$ addExtensionContent(bundleData, type, getExtClassLoader(), addExtURLMethod); } private void addExtensionContent(BaseData bundleData, byte type, ClassLoader addToLoader, Method addToMethod) { if ((type & (EXTENSION_UNINSTALLED | EXTENSION_UPDATED)) != 0) // if uninstalled or updated then do nothing framework must be restarted. return; // first make sure this BundleData is not on the pre-configured osgi.framework.extensions list String[] extensions = getConfiguredExtensions(); for (int i = 0; i < extensions.length; i++) if (extensions[i].equals(bundleData.getSymbolicName())) return; if ((type & EXTENSION_INSTALLED) != 0) { if (extensionListener == null) { // add bundle listener to wait for extension to be resolved extensionListener = this; context.addBundleListener(extensionListener); } return; } File[] files = getExtensionFiles(bundleData); if (files == null) return; for (int i = 0; i < files.length; i++) { if (files[i] == null) continue; try { callAddURLMethod(addToLoader, addToMethod, AdaptorUtil.encodeFileURL(files[i])); } catch (InvocationTargetException e) { adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e); } catch (MalformedURLException e) { adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e); } } try { addToLoader.loadClass("thisIsNotAClass"); // initialize the new urls //$NON-NLS-1$ } catch (ClassNotFoundException e) { // do nothing } } /** * Returns a list of configured extensions * @return a list of configured extensions */ protected String[] getConfiguredExtensions() { if (configuredExtensions != null) return configuredExtensions; String prop = FrameworkProperties.getProperty(BaseStorage.PROP_FRAMEWORK_EXTENSIONS); if (prop == null || prop.trim().length() == 0) configuredExtensions = new String[0]; else configuredExtensions = ManifestElement.getArrayFromList(prop); return configuredExtensions; } /** * Processes a boot extension bundle * @param bundleData the extension bundle data * @param type the type of extension bundle * @throws BundleException on errors or if boot extensions are not supported */ protected void processBootExtension(BundleData bundleData, byte type) throws BundleException { throw new BundleException("Boot classpath extensions are not supported.", BundleException.UNSUPPORTED_OPERATION, new UnsupportedOperationException()); //$NON-NLS-1$ } private void initBundleStoreRoot() { File configurationLocation = LocationManager.getOSGiConfigurationDir(); if (configurationLocation != null) bundleStoreRoot = new File(configurationLocation, LocationManager.BUNDLES_DIR); else // last resort just default to "bundles" bundleStoreRoot = new File(LocationManager.BUNDLES_DIR); } public File getBundleStoreRoot() { if (bundleStoreRoot == null) initBundleStoreRoot(); return bundleStoreRoot; } /** * Returns a list of classpath files for an extension bundle * @param bundleData the bundle data for an extension bundle * @return a list of classpath files for an extension bundle */ protected File[] getExtensionFiles(BaseData bundleData) { File[] files = null; try { String[] paths = bundleData.getClassPath(); if (DevClassPathHelper.inDevelopmentMode()) { String[] devPaths = DevClassPathHelper.getDevClassPath(bundleData.getSymbolicName()); String[] origPaths = paths; paths = new String[origPaths.length + devPaths.length]; System.arraycopy(origPaths, 0, paths, 0, origPaths.length); System.arraycopy(devPaths, 0, paths, origPaths.length, devPaths.length); } List<File> results = new ArrayList<File>(paths.length); for (int i = 0; i < paths.length; i++) { if (".".equals(paths[i])) //$NON-NLS-1$ results.add(bundleData.getBundleFile().getBaseFile()); else { File result = bundleData.getBundleFile().getFile(paths[i], false); if (result != null) results.add(result); } } return results.toArray(new File[results.size()]); } catch (BundleException e) { adaptor.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, bundleData.getBundle(), e); } return files; } void requestSave() { // Only when the State saver is enabled will the stateSaver be started. if (stateSaver == null) return; stateSaver.requestSave(); } /** * Updates the state mananager with an updated/installed/uninstalled bundle * @param bundleData the modified bundle * @param type the type of modification * @throws BundleException */ public void updateState(BaseData bundleData, int type) throws BundleException { if (stateManager == null) { invalidState = true; return; } State systemState = stateManager.getSystemState(); BundleDescription oldDescription = null; BundleDescription newDescription = null; switch (type) { case BundleEvent.UPDATED : // fall through to INSTALLED case BundleEvent.INSTALLED : if (type == BundleEvent.UPDATED) oldDescription = systemState.getBundle(bundleData.getBundleID()); newDescription = stateManager.getFactory().createBundleDescription(systemState, bundleData.getManifest(), bundleData.getLocation(), bundleData.getBundleID()); // For the install case we need to set the bundle before adding to the state // because the bundle is not available in the context yet. // We go ahead and set it for the update case for simplicity; // but this is not strictly necessary newDescription.setUserObject(bundleData); if (oldDescription == null) systemState.addBundle(newDescription); else systemState.updateBundle(newDescription); break; case BundleEvent.UNINSTALLED : systemState.removeBundle(bundleData.getBundleID()); break; } if (newDescription != null) validateNativeCodePaths(newDescription, bundleData); } private void validateNativeCodePaths(BundleDescription newDescription, BaseData data) { NativeCodeSpecification nativeCode = newDescription.getNativeCodeSpecification(); if (nativeCode == null) return; NativeCodeDescription nativeCodeDescs[] = nativeCode.getPossibleSuppliers(); for (int i = 0; i < nativeCodeDescs.length; i++) { BaseStorageHook storageHook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); if (storageHook != null) try { storageHook.validateNativePaths(nativeCodeDescs[i].getNativePaths()); } catch (BundleException e) { stateManager.getSystemState().setNativePathsInvalid(nativeCodeDescs[i], true); } } } private class StateSaver implements Runnable { private final long delay_interval; private final long max_total_delay_interval; private boolean shutdown = false; private long lastSaveTime = 0; private Thread runningThread = null; private Thread shutdownHook = null; StateSaver() { String prop = FrameworkProperties.getProperty("eclipse.stateSaveDelayInterval"); //$NON-NLS-1$ long delayValue = 30000; // 30 seconds. long maxDelayValue = 1800000; // 30 minutes. if (prop != null) { try { long val = Long.parseLong(prop); if (val >= 1000 && val <= 1800000) { delayValue = val; maxDelayValue = val * 60; } else if (val == 0) { delayValue = 0; maxDelayValue = 0; } } catch (NumberFormatException e) { // ignore } } delay_interval = delayValue; max_total_delay_interval = maxDelayValue; } public void run() { State systemState = adaptor.getState(); synchronized (systemState) { long firstSaveTime = lastSaveTime; long curSaveTime = 0; long delayTime; do { do { if ((System.currentTimeMillis() - firstSaveTime) > max_total_delay_interval) { curSaveTime = lastSaveTime; // Waiting time has been too long, so break to start saving State data to file. break; } delayTime = Math.min(delay_interval, lastSaveTime - curSaveTime); curSaveTime = lastSaveTime; // wait for other save requests try { if (!shutdown) systemState.wait(delayTime); } catch (InterruptedException ie) { // force break from do/while loops curSaveTime = lastSaveTime; break; } // Continue the loop if 'lastSaveTime' is increased again during waiting. } while (!shutdown && curSaveTime < lastSaveTime); // Save State and Meta data. saveAllData(false); // Continue the loop if Saver is asked again during saving State data to file. } while (!shutdown && curSaveTime < lastSaveTime); runningThread = null; // clear runningThread try { Runtime.getRuntime().removeShutdownHook(shutdownHook); } catch (IllegalStateException e) { // avoid exception if shutdown is in progress } shutdownHook = null; } } void shutdown() { State systemState = adaptor.getState(); Thread joinWith = null; synchronized (systemState) { shutdown = true; joinWith = runningThread; systemState.notifyAll(); // To wakeup sleeping thread. } try { if (joinWith != null) { if (Debug.DEBUG_GENERAL) Debug.println("About to join saving thread"); //$NON-NLS-1$ // There should be no deadlock when 'shutdown' is true. joinWith.join(); if (Debug.DEBUG_GENERAL) Debug.println("Joined with saving thread"); //$NON-NLS-1$ } } catch (InterruptedException ie) { if (Debug.DEBUG_GENERAL) { Debug.println("Error shutdowning StateSaver: " + ie.getMessage()); //$NON-NLS-1$ Debug.printStackTrace(ie); } } } void requestSave() { final State systemState = adaptor.getState(); synchronized (systemState) { if (shutdown) return; // do not start another thread if we have already shutdown if (delay_interval == 0) { // all saves are atomic; never start a background thread saveAllData(false); return; } lastSaveTime = System.currentTimeMillis(); if (runningThread == null) { shutdownHook = new Thread(new Runnable() { public void run() { // Synchronize with JVM shutdown hook, because // saveAllData creates a temp file with delete on // exit is true. The temp file will be removed in the // shutdown hook. This prevents that the remove temp files // in the shutdown hook is earlier handled then adding new // temp file in saveAllData. shutdown(); } }); runningThread = new Thread(this, "State Saver"); //$NON-NLS-1$ runningThread.start(); Runtime.getRuntime().addShutdownHook(shutdownHook); } } } } public long getNextBundleId() { synchronized (this.nextIdMonitor) { return nextId++; } } public void bundleChanged(BundleEvent event) { if (event.getType() != BundleEvent.RESOLVED) return; BaseData data = (BaseData) ((AbstractBundle) event.getBundle()).getBundleData(); try { if ((data.getType() & BundleData.TYPE_FRAMEWORK_EXTENSION) != 0) processFrameworkExtension(data, EXTENSION_INITIALIZE); else if ((data.getType() & BundleData.TYPE_BOOTCLASSPATH_EXTENSION) != 0) processBootExtension(data, EXTENSION_INITIALIZE); else if ((data.getType() & BundleData.TYPE_EXTCLASSPATH_EXTENSION) != 0) processExtExtension(data, EXTENSION_INITIALIZE); } catch (BundleException e) { // do nothing; } } public String copyToTempLibrary(BaseData data, String absolutePath) throws IOException { File storageRoot = getBundleStoreRoot(); File libTempDir = new File(storageRoot, 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(data.getBundleID()); for (int i = 0; i < Integer.MAX_VALUE; i++) { bundleTempDir = new File(libTempDir, bundleID.toString() + "_" + new Integer(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.exists()) { 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, BaseStorage.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 InputStream in = new FileInputStream(realLib); AdaptorUtil.readFile(in, libTempFile); // set permissions if needed BundleFile.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(); } }