| /******************************************************************************* |
| * Copyright (c) 2005, 2010 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 253942) |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.internal.baseadaptor; |
| |
| import java.io.*; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.util.*; |
| import org.eclipse.core.runtime.adaptor.LocationManager; |
| import org.eclipse.osgi.baseadaptor.BaseAdaptor; |
| import org.eclipse.osgi.baseadaptor.BaseData; |
| import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry; |
| import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook; |
| import org.eclipse.osgi.baseadaptor.hooks.StorageHook; |
| import org.eclipse.osgi.framework.adaptor.*; |
| import org.eclipse.osgi.framework.debug.Debug; |
| import org.eclipse.osgi.framework.internal.core.*; |
| import org.eclipse.osgi.framework.internal.core.Constants; |
| import org.eclipse.osgi.framework.log.FrameworkLog; |
| import org.eclipse.osgi.framework.util.KeyedElement; |
| import org.eclipse.osgi.service.datalocation.Location; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.*; |
| |
| public class BaseStorageHook implements StorageHook, AdaptorHook { |
| public static final String KEY = BaseStorageHook.class.getName(); |
| public static final int HASHCODE = KEY.hashCode(); |
| public static final int DEL_BUNDLE_STORE = 0x01; |
| public static final int DEL_GENERATION = 0x02; |
| private static final int STORAGE_VERSION = 1; |
| public static final String EXTERNAL_LIB_PREFIX = "external:"; //$NON-NLS-1$ |
| public static final String VARIABLE_DELIM_STRING = "$"; //$NON-NLS-1$ |
| public static final char VARIABLE_DELIM_CHAR = '$'; |
| public static String COMPOSITE_HEADER = "Equinox-CompositeBundle"; //$NON-NLS-1$ |
| public static String COMPOSITE_BUNDLE = "composite"; //$NON-NLS-1$ |
| public static String SURROGATE_BUNDLE = "surrogate"; //$NON-NLS-1$ |
| |
| /** bundle's file name */ |
| private String fileName; |
| /** native code paths for this BundleData */ |
| private String[] nativePaths; |
| /** bundle generation */ |
| private int generation = 1; |
| /** Is bundle a reference */ |
| private boolean reference; |
| |
| private BaseData bundleData; |
| private BaseStorage storage; |
| private File bundleStore; |
| private File dataStore; |
| |
| public BaseStorageHook(BaseStorage storage) { |
| this.storage = storage; |
| } |
| |
| public int getStorageVersion() { |
| return STORAGE_VERSION; |
| } |
| |
| public StorageHook create(BaseData bundledata) throws BundleException { |
| BaseStorageHook storageHook = new BaseStorageHook(storage); |
| storageHook.bundleData = bundledata; |
| return storageHook; |
| } |
| |
| public void initialize(Dictionary manifest) throws BundleException { |
| BaseStorageHook.loadManifest(bundleData, manifest); |
| } |
| |
| static void loadManifest(BaseData target, Dictionary manifest) throws BundleException { |
| try { |
| target.setVersion(Version.parseVersion((String) manifest.get(Constants.BUNDLE_VERSION))); |
| } catch (IllegalArgumentException e) { |
| target.setVersion(new InvalidVersion((String) manifest.get(Constants.BUNDLE_VERSION))); |
| } |
| ManifestElement[] bsnHeader = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME)); |
| int bundleType = 0; |
| if (bsnHeader != null) { |
| target.setSymbolicName(bsnHeader[0].getValue()); |
| String singleton = bsnHeader[0].getDirective(Constants.SINGLETON_DIRECTIVE); |
| if (singleton == null) |
| singleton = bsnHeader[0].getAttribute(Constants.SINGLETON_DIRECTIVE); |
| if ("true".equals(singleton)) //$NON-NLS-1$ |
| bundleType |= BundleData.TYPE_SINGLETON; |
| } |
| // check that the classpath is valid |
| String classpath = (String) manifest.get(Constants.BUNDLE_CLASSPATH); |
| ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, classpath); |
| target.setClassPathString(classpath); |
| target.setActivator((String) manifest.get(Constants.BUNDLE_ACTIVATOR)); |
| String host = (String) manifest.get(Constants.FRAGMENT_HOST); |
| if (host != null) { |
| bundleType |= BundleData.TYPE_FRAGMENT; |
| ManifestElement[] hostElement = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, host); |
| if (Constants.getInternalSymbolicName().equals(hostElement[0].getValue()) || Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(hostElement[0].getValue())) { |
| String extensionType = hostElement[0].getDirective("extension"); //$NON-NLS-1$ |
| if (extensionType == null || extensionType.equals("framework")) //$NON-NLS-1$ |
| bundleType |= BundleData.TYPE_FRAMEWORK_EXTENSION; |
| else if (extensionType.equals("bootclasspath")) //$NON-NLS-1$ |
| bundleType |= BundleData.TYPE_BOOTCLASSPATH_EXTENSION; |
| else if (extensionType.equals("extclasspath")) //$NON-NLS-1$ |
| bundleType |= BundleData.TYPE_EXTCLASSPATH_EXTENSION; |
| } |
| } else { |
| String composite = (String) manifest.get(COMPOSITE_HEADER); |
| if (composite != null) { |
| if (COMPOSITE_BUNDLE.equals(composite)) |
| bundleType |= BundleData.TYPE_COMPOSITEBUNDLE; |
| else |
| bundleType |= BundleData.TYPE_SURROGATEBUNDLE; |
| } |
| } |
| target.setType(bundleType); |
| target.setExecutionEnvironment((String) manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT)); |
| target.setDynamicImports((String) manifest.get(Constants.DYNAMICIMPORT_PACKAGE)); |
| } |
| |
| public StorageHook load(BaseData target, DataInputStream in) throws IOException { |
| target.setLocation(AdaptorUtil.readString(in, false)); |
| target.setSymbolicName(AdaptorUtil.readString(in, false)); |
| target.setVersion(AdaptorUtil.loadVersion(in)); |
| target.setActivator(AdaptorUtil.readString(in, false)); |
| target.setClassPathString(AdaptorUtil.readString(in, false)); |
| target.setExecutionEnvironment(AdaptorUtil.readString(in, false)); |
| target.setDynamicImports(AdaptorUtil.readString(in, false)); |
| target.setStartLevel(in.readInt()); |
| target.setStatus(in.readInt()); |
| target.setType(in.readInt()); |
| target.setLastModified(in.readLong()); |
| target.setDirty(false); // make sure to reset the dirty bit; |
| |
| BaseStorageHook storageHook = new BaseStorageHook(storage); |
| storageHook.bundleData = target; |
| storageHook.generation = in.readInt(); |
| storageHook.reference = in.readBoolean(); |
| storageHook.setFileName(getAbsolute(storageHook.reference, AdaptorUtil.readString(in, false))); |
| int nativePathCount = in.readInt(); |
| storageHook.nativePaths = nativePathCount > 0 ? new String[nativePathCount] : null; |
| for (int i = 0; i < nativePathCount; i++) |
| storageHook.nativePaths[i] = in.readUTF(); |
| return storageHook; |
| } |
| |
| private String getAbsolute(boolean isReference, String path) { |
| if (!isReference) |
| return path; |
| // fileName for bundles installed with reference URLs is stored relative to the install location |
| File storedPath = new File(path); |
| if (!storedPath.isAbsolute()) |
| // make sure it has the absolute location instead |
| return new FilePath(storage.getInstallPath() + path).toString(); |
| return path; |
| } |
| |
| public void save(DataOutputStream out) throws IOException { |
| if (bundleData == null) |
| throw new IllegalStateException(); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getLocation()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getSymbolicName()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getVersion().toString()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getActivator()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getClassPathString()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getExecutionEnvironment()); |
| AdaptorUtil.writeStringOrNull(out, bundleData.getDynamicImports()); |
| StorageHook[] hooks = bundleData.getStorageHooks(); |
| boolean forgetStartLevel = false; |
| for (int i = 0; i < hooks.length && !forgetStartLevel; i++) |
| forgetStartLevel = hooks[i].forgetStartLevelChange(bundleData.getStartLevel()); |
| out.writeInt(!forgetStartLevel ? bundleData.getStartLevel() : 1); |
| boolean forgetStatus = false; |
| // see if we should forget the persistently started flag |
| for (int i = 0; i < hooks.length && !forgetStatus; i++) |
| forgetStatus = hooks[i].forgetStatusChange(bundleData.getStatus()); |
| out.writeInt(!forgetStatus ? bundleData.getStatus() : (~Constants.BUNDLE_STARTED) & bundleData.getStatus()); |
| out.writeInt(bundleData.getType()); |
| out.writeLong(bundleData.getLastModified()); |
| |
| out.writeInt(getGeneration()); |
| out.writeBoolean(isReference()); |
| String storedFileName = isReference() ? new FilePath(storage.getInstallPath()).makeRelative(new FilePath(getFileName())) : getFileName(); |
| AdaptorUtil.writeStringOrNull(out, storedFileName); |
| if (nativePaths == null) |
| out.writeInt(0); |
| else { |
| out.writeInt(nativePaths.length); |
| for (int i = 0; i < nativePaths.length; i++) |
| out.writeUTF(nativePaths[i]); |
| } |
| |
| } |
| |
| public int getKeyHashCode() { |
| return HASHCODE; |
| } |
| |
| public boolean compare(KeyedElement other) { |
| return other.getKey() == KEY; |
| } |
| |
| public Object getKey() { |
| return KEY; |
| } |
| |
| public String getFileName() { |
| return fileName; |
| } |
| |
| public int getGeneration() { |
| return generation; |
| } |
| |
| public String[] getNativePaths() { |
| return nativePaths; |
| } |
| |
| public void installNativePaths(String[] installPaths) throws BundleException { |
| validateNativePaths(installPaths); |
| this.nativePaths = installPaths; |
| } |
| |
| public void validateNativePaths(String[] nativePaths) throws BundleException { |
| for (int i = 0; i < nativePaths.length; i++) { |
| if (nativePaths[i].startsWith(EXTERNAL_LIB_PREFIX)) { |
| String path = substituteVars(nativePaths[i].substring(EXTERNAL_LIB_PREFIX.length())); |
| File nativeFile = new File(path); |
| if (!nativeFile.exists()) |
| throw new BundleException(NLS.bind(AdaptorMsg.BUNDLE_NATIVECODE_EXCEPTION, nativeFile.getAbsolutePath()), BundleException.NATIVECODE_ERROR); |
| continue; // continue to next path |
| } |
| // ensure the file exists in the bundle; it will get extracted later on demand |
| BundleEntry nativeEntry = bundleData.getBundleFile().getEntry(nativePaths[i]); |
| if (nativeEntry == null) |
| throw new BundleException(NLS.bind(AdaptorMsg.BUNDLE_NATIVECODE_EXCEPTION, nativePaths[i]), BundleException.NATIVECODE_ERROR); |
| } |
| } |
| |
| public boolean isReference() { |
| return reference; |
| } |
| |
| public File getBundleStore() { |
| if (bundleStore == null) |
| bundleStore = new File(storage.getBundleStoreRoot(), String.valueOf(bundleData.getBundleID())); |
| return bundleStore; |
| } |
| |
| public File getDataFile(String path) { |
| // lazily initialize dirData to prevent early access to configuration location |
| if (dataStore == null) |
| dataStore = new File(getBundleStore(), BaseStorage.DATA_DIR_NAME); |
| if (path != null && !dataStore.exists() && (storage.isReadOnly() || !dataStore.mkdirs())) |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) |
| Debug.println("Unable to create bundle data directory: " + dataStore.getPath()); //$NON-NLS-1$ |
| return path == null ? dataStore : new File(dataStore, path); |
| } |
| |
| void delete(boolean postpone, int type) throws IOException { |
| File delete = null; |
| switch (type) { |
| case DEL_GENERATION : |
| delete = getGenerationDir(); |
| break; |
| case DEL_BUNDLE_STORE : |
| delete = getBundleStore(); |
| break; |
| } |
| if (delete != null && delete.exists() && (postpone || !AdaptorUtil.rm(delete))) { |
| /* create .delete */ |
| FileOutputStream out = new FileOutputStream(new File(delete, BaseStorage.DELETE_FLAG)); |
| out.close(); |
| } |
| } |
| |
| File getGenerationDir() { |
| return new File(getBundleStore(), String.valueOf(getGeneration())); |
| } |
| |
| File getParentGenerationDir() { |
| Location parentConfiguration = null; |
| Location currentConfiguration = LocationManager.getConfigurationLocation(); |
| if (currentConfiguration != null && (parentConfiguration = currentConfiguration.getParentLocation()) != null) |
| return new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + '/' + LocationManager.BUNDLES_DIR + '/' + bundleData.getBundleID() + '/' + getGeneration()); |
| return null; |
| } |
| |
| File createGenerationDir() { |
| File generationDir = getGenerationDir(); |
| if (!generationDir.exists() && (storage.isReadOnly() || !generationDir.mkdirs())) |
| if (Debug.DEBUG && Debug.DEBUG_GENERAL) |
| Debug.println("Unable to create bundle generation directory: " + generationDir.getPath()); //$NON-NLS-1$ |
| return generationDir; |
| } |
| |
| public void setReference(boolean reference) { |
| this.reference = reference; |
| } |
| |
| public void setFileName(String fileName) { |
| this.fileName = fileName; |
| // This is only done for PDE source lookup (bug 126517) |
| this.bundleData.setFileName(fileName); |
| } |
| |
| public void copy(StorageHook storageHook) { |
| if (!(storageHook instanceof BaseStorageHook)) |
| throw new IllegalArgumentException(); |
| BaseStorageHook hook = (BaseStorageHook) storageHook; |
| bundleStore = hook.bundleStore; |
| dataStore = hook.dataStore; |
| generation = hook.generation + 1; |
| // fileName and reference will be set by update |
| } |
| |
| public void validate() throws IllegalArgumentException { |
| // do nothing |
| } |
| |
| public Dictionary getManifest(boolean firstLoad) throws BundleException { |
| // do nothing |
| return null; |
| } |
| |
| public boolean forgetStatusChange(int status) { |
| // do nothing |
| return false; |
| } |
| |
| public boolean forgetStartLevelChange(int startlevel) { |
| // do nothing |
| return false; |
| } |
| |
| public void initialize(BaseAdaptor adaptor) { |
| // do nothing |
| } |
| |
| public void frameworkStart(BundleContext context) throws BundleException { |
| // do nothing |
| } |
| |
| public void frameworkStop(BundleContext context) throws BundleException { |
| // do nothing |
| } |
| |
| public void frameworkStopping(BundleContext context) { |
| // do nothing |
| } |
| |
| public void addProperties(Properties properties) { |
| // do nothing |
| } |
| |
| public URLConnection mapLocationToURLConnection(String location) throws IOException { |
| // take into account that initial@ is special (bug 268563) |
| if (location.startsWith("initial@")) { //$NON-NLS-1$ |
| location = location.substring(8); |
| return new URL(location).openConnection(); |
| } |
| // see if this is an existing location |
| Bundle[] bundles = storage.getAdaptor().getContext().getBundles(); |
| AbstractBundle bundle = null; |
| for (int i = 0; i < bundles.length && bundle == null; i++) |
| if (location.equals(bundles[i].getLocation())) |
| bundle = (AbstractBundle) bundles[i]; |
| if (bundle == null) |
| return null; |
| BaseData data = (BaseData) bundle.getBundleData(); |
| BaseStorageHook hook = (BaseStorageHook) data.getStorageHook(BaseStorageHook.KEY); |
| return hook.isReference() ? new URL("reference:file:" + hook.getFileName()).openConnection() : null; //$NON-NLS-1$ |
| } |
| |
| public void handleRuntimeError(Throwable error) { |
| // do nothing |
| } |
| |
| public FrameworkLog createFrameworkLog() { |
| // do nothing |
| return null; |
| } |
| |
| public BaseStorage getStorage() { |
| return storage; |
| } |
| |
| public static String substituteVars(String path) { |
| StringBuffer buf = new StringBuffer(path.length()); |
| StringTokenizer st = new StringTokenizer(path, VARIABLE_DELIM_STRING, true); |
| boolean varStarted = false; // indicates we are processing a var subtitute |
| String var = null; // the current var key |
| while (st.hasMoreElements()) { |
| String tok = st.nextToken(); |
| if (VARIABLE_DELIM_STRING.equals(tok)) { |
| if (!varStarted) { |
| varStarted = true; // we found the start of a var |
| var = ""; //$NON-NLS-1$ |
| } else { |
| // we have found the end of a var |
| String prop = null; |
| // get the value of the var from system properties |
| if (var != null && var.length() > 0) |
| prop = FrameworkProperties.getProperty(var); |
| if (prop == null) { |
| try { |
| // try using the System.getenv method if it exists (bug 126921) |
| Method getenv = System.class.getMethod("getenv", new Class[] {String.class}); //$NON-NLS-1$ |
| prop = (String) getenv.invoke(null, new Object[] {var}); |
| } catch (Throwable t) { |
| // do nothing; |
| // on 1.4 VMs this throws an error |
| // on J2ME this method does not exist |
| } |
| } |
| if (prop != null) |
| // found a value; use it |
| buf.append(prop); |
| else |
| // could not find a value append the var name w/o delims |
| buf.append(var == null ? "" : var); //$NON-NLS-1$ |
| varStarted = false; |
| var = null; |
| } |
| } else { |
| if (!varStarted) |
| buf.append(tok); // the token is not part of a var |
| else |
| var = tok; // the token is the var key; save the key to process when we find the end token |
| } |
| } |
| if (var != null) |
| // found a case of $var at the end of the path with no trailing $; just append it as is. |
| buf.append(VARIABLE_DELIM_CHAR).append(var); |
| return buf.toString(); |
| } |
| } |