| /******************************************************************************* |
| * Copyright (c) 2003, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.runtime.adaptor; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.util.*; |
| import org.eclipse.osgi.framework.adaptor.FrameworkAdaptor; |
| import org.eclipse.osgi.framework.adaptor.core.BundleEntry; |
| import org.eclipse.osgi.framework.internal.core.Constants; |
| import org.eclipse.osgi.framework.internal.defaultadaptor.DefaultAdaptor; |
| import org.eclipse.osgi.framework.internal.defaultadaptor.DefaultBundleData; |
| import org.eclipse.osgi.framework.log.FrameworkLogEntry; |
| import org.eclipse.osgi.framework.util.Headers; |
| import org.eclipse.osgi.service.datalocation.Location; |
| import org.eclipse.osgi.service.pluginconversion.PluginConversionException; |
| import org.eclipse.osgi.service.resolver.Version; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.osgi.framework.BundleException; |
| |
| /** |
| * Internal class. |
| */ |
| //TODO This class does not override save(). |
| //Maybe for consistency should it be overriden to do nothing. See also EclipseAdaptor.saveMetadataFor(BundleData) |
| public class EclipseBundleData extends DefaultBundleData { |
| static final byte MANIFEST_TYPE_UNKNOWN = 0x00; |
| static final byte MANIFEST_TYPE_BUNDLE = 0x01; |
| static final byte MANIFEST_TYPE_PLUGIN = 0x02; |
| static final byte MANIFEST_TYPE_FRAGMENT = 0x04; |
| static final byte MANIFEST_TYPE_JAR = 0x08; |
| |
| private static String[] libraryVariants = null; |
| |
| /** data to detect modification made in the manifest */ |
| private long manifestTimeStamp = 0; |
| private byte manifestType = MANIFEST_TYPE_UNKNOWN; |
| |
| // URL protocol designations |
| public static final String PROTOCOL = "platform"; //$NON-NLS-1$ |
| public static final String FILE = "file"; //$NON-NLS-1$ |
| |
| private static final String PROP_CHECK_CONFIG = "osgi.checkConfiguration"; //$NON-NLS-1$ |
| // the Plugin-Class header |
| protected String pluginClass = null; |
| // the Eclipse-AutoStart header |
| private boolean autoStart; |
| private String[] autoStartExceptions; |
| |
| private static String[] buildLibraryVariants() { |
| ArrayList result = new ArrayList(); |
| EnvironmentInfo info = EnvironmentInfo.getDefault(); |
| result.add("ws/" + info.getWS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ |
| result.add("os/" + info.getOS() + "/" + info.getOSArch() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| result.add("os/" + info.getOS() + "/"); //$NON-NLS-1$ //$NON-NLS-2$ |
| String nl = info.getNL(); |
| nl = nl.replace('_', '/'); |
| while (nl.length() > 0) { |
| result.add("nl/" + nl + "/"); //$NON-NLS-1$ //$NON-NLS-2$ |
| int i = nl.lastIndexOf('/'); |
| nl = (i < 0) ? "" : nl.substring(0, i); //$NON-NLS-1$ |
| } |
| result.add(""); //$NON-NLS-1$ |
| return (String[]) result.toArray(new String[result.size()]); |
| } |
| |
| public EclipseBundleData(DefaultAdaptor adaptor, long id) throws IOException { |
| super(adaptor, id); |
| } |
| |
| public void initializeExistingBundle() throws IOException { |
| File delete = new File(getBundleStoreDir(), ".delete"); //$NON-NLS-1$ |
| |
| /* and the directory is not marked for delete */ |
| if (delete.exists()) |
| throw new IOException(); |
| |
| createBaseBundleFile(); |
| if (!checkManifestTimeStamp()) |
| throw new IOException(); |
| } |
| |
| private boolean checkManifestTimeStamp() { |
| if (!"true".equalsIgnoreCase(System.getProperty(PROP_CHECK_CONFIG))) //$NON-NLS-1$ |
| return true; |
| |
| return PluginConverterImpl.getTimeStamp(getBaseFile(), getManifestType()) == getManifestTimeStamp(); |
| } |
| |
| /** |
| * Returns the absolute path name of a native library. The VM invokes this |
| * method to locate the native libraries that belong to classes loaded with |
| * this class loader. If this method returns <code>null</code>, the VM |
| * searches the library along the path specified as the <code>java.library.path</code> |
| * property. |
| * |
| * @param libName |
| * the library name |
| * @return the absolute path of the native library |
| */ |
| public String findLibrary(String libName) { |
| // first do the standard OSGi lookup using the native clauses |
| // in the manifest. If that fails, do the legacy Eclipse lookup. |
| String result = super.findLibrary(libName); |
| if (result != null) |
| return result; |
| if (libraryVariants == null) |
| libraryVariants = buildLibraryVariants(); |
| if (libName.length() == 0) |
| return null; |
| if (libName.charAt(0) == '/' || libName.charAt(0) == '\\') |
| libName = libName.substring(1); |
| libName = System.mapLibraryName(libName); |
| |
| // if (DEBUG && DEBUG_SHOW_ACTIONS && debugNative(libName)) |
| // debug("findLibrary(" + libName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| return searchVariants(libraryVariants, libName); |
| |
| } |
| |
| private String searchVariants(String[] variants, String path) { |
| for (int i = 0; i < variants.length; i++) { |
| BundleEntry libEntry = baseBundleFile.getEntry(variants[i] + path); |
| if (libEntry == null) { |
| // if (DEBUG && DEBUG_SHOW_FAILURE) |
| // debug("not found " + variants[i] + path); |
| // //$NON-NLS-1$ |
| } else { |
| // if (DEBUG && DEBUG_SHOW_SUCCESS) |
| // debug("found " + path + " as " + |
| // variants[i] + path); //$NON-NLS-1$ //$NON-NLS-2$ |
| File libFile = baseBundleFile.getFile(variants[i] + path); |
| return libFile.getAbsolutePath(); |
| } |
| } |
| return null; |
| } |
| |
| //TODO Unused method |
| private URL[] getSearchURLs(URL target) { |
| return new URL[] {target}; |
| } |
| |
| public synchronized Dictionary getManifest() throws BundleException { |
| return getManifest(false); |
| } |
| |
| public synchronized Dictionary getManifest(boolean first) throws BundleException { |
| if (manifest == null) |
| manifest = first ? loadManifest() : new CachedManifest(this); |
| if (manifest.get(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME) == null) { |
| if (getEntry(PluginConverterImpl.PLUGIN_MANIFEST) != null || getEntry(PluginConverterImpl.FRAGMENT_MANIFEST) != null) { |
| Dictionary generatedManifest = generateManifest(manifest); |
| if (generatedManifest != null) |
| manifest = generatedManifest; |
| } |
| } |
| return manifest; |
| } |
| |
| public synchronized Dictionary loadManifest() throws BundleException { |
| URL url = getEntry(Constants.OSGI_BUNDLE_MANIFEST); |
| if (url != null) { |
| manifestTimeStamp = getBaseBundleFile().getEntry(Constants.OSGI_BUNDLE_MANIFEST).getTime(); |
| manifestType = MANIFEST_TYPE_BUNDLE; |
| return loadManifestFrom(url); |
| } |
| Dictionary result = generateManifest(null); |
| if (result == null) |
| throw new BundleException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_DATA_MANIFEST_NOT_FOUND", getLocation())); //$NON-NLS-1$ |
| return result; |
| } |
| |
| private Headers basicCheckManifest(String cacheLocation, String symbolicName, String version, byte inputType) throws BundleException { |
| File currentFile = new File(cacheLocation, symbolicName + '_' + version + ".MF"); //$NON-NLS-1$ |
| if (PluginConverterImpl.upToDate(currentFile, getBaseFile(), inputType)) { |
| try { |
| return Headers.parseManifest(new FileInputStream(currentFile)); |
| } catch (FileNotFoundException e) { |
| // do nothing. |
| } |
| } |
| return null; |
| } |
| |
| private Headers checkManifestAndParent(String cacheLocation, String symbolicName, String version, byte inputType) throws BundleException { |
| Headers result = basicCheckManifest(cacheLocation, symbolicName, version, inputType); |
| if (result != null) |
| return result; |
| |
| Location parentConfiguration = null; |
| if ((parentConfiguration = LocationManager.getConfigurationLocation().getParentLocation()) != null) { |
| result = basicCheckManifest(new File(parentConfiguration.getURL().getFile(), FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME + '/' + LocationManager.MANIFESTS_DIR).toString(), symbolicName, version, inputType); |
| } |
| return result; |
| } |
| |
| private Dictionary generateManifest(Dictionary originalManifest) throws BundleException { |
| String cacheLocation = System.getProperty(LocationManager.PROP_MANIFEST_CACHE); |
| if (getSymbolicName() != null) { |
| Headers existingHeaders = checkManifestAndParent(cacheLocation, getSymbolicName(), getVersion().toString(), manifestType); |
| if (existingHeaders != null) |
| return existingHeaders; |
| } |
| |
| PluginConverterImpl converter = PluginConverterImpl.getDefault(); |
| |
| Dictionary generatedManifest; |
| try { |
| generatedManifest = converter.convertManifest(getBaseFile(), true, null, true); |
| } catch (PluginConversionException pce) { |
| String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_CONVERTER_ERROR_CONVERTING", getBaseFile()); //$NON-NLS-1$ |
| throw new BundleException(message, pce); //$NON-NLS-1$ |
| } |
| |
| //Now we know the symbolicId and the version of the bundle, we check to see if don't have a manifest for it already |
| Version version = new Version((String) generatedManifest.get(Constants.BUNDLE_VERSION)); |
| String symbolicName = ManifestElement.parseHeader(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME, (String) generatedManifest.get(org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME))[0].getValue(); |
| ManifestElement generatedFrom = ManifestElement.parseHeader(PluginConverterImpl.GENERATED_FROM, (String) generatedManifest.get(PluginConverterImpl.GENERATED_FROM))[0]; |
| Headers existingHeaders = checkManifestAndParent(cacheLocation, symbolicName, version.toString(), Byte.parseByte(generatedFrom.getAttribute(PluginConverterImpl.MANIFEST_TYPE_ATTRIBUTE))); |
| if (existingHeaders != null) |
| return existingHeaders; |
| |
| //We don't have a manifest. |
| setManifestTimeStamp(Long.parseLong(generatedFrom.getValue())); |
| setManifestType(Byte.parseByte(generatedFrom.getAttribute(PluginConverterImpl.MANIFEST_TYPE_ATTRIBUTE))); |
| |
| //merge the original manifest with the generated one |
| if (originalManifest != null) { |
| Enumeration enum = originalManifest.keys(); |
| while (enum.hasMoreElements()) { |
| Object key = enum.nextElement(); |
| generatedManifest.put(key, originalManifest.get(key)); |
| } |
| } |
| |
| //write the generated manifest |
| File bundleManifestLocation = new File(cacheLocation, symbolicName + '_' + version.toString() + ".MF"); //$NON-NLS-1$ |
| try { |
| converter.writeManifest(bundleManifestLocation, generatedManifest, true); |
| } catch (Exception e) { |
| //TODO Need to log |
| } |
| return generatedManifest; |
| |
| } |
| |
| private Dictionary loadManifestFrom(URL manifestURL) throws BundleException { |
| try { |
| return Headers.parseManifest(manifestURL.openStream()); |
| } catch (IOException e) { |
| throw new BundleException(EclipseAdaptorMsg.formatter.getString("ECLIPSE_DATA_ERROR_READING_MANIFEST", getLocation()), e); //$NON-NLS-1$ |
| } |
| } |
| |
| protected void loadFromManifest() throws IOException, BundleException { |
| getManifest(true); |
| super.loadFromManifest(); |
| // manifest cannot ever be a cached one otherwise the lines below are bogus |
| if (manifest instanceof CachedManifest) |
| throw new IllegalStateException(); |
| pluginClass = (String) manifest.get(EclipseAdaptor.PLUGIN_CLASS); |
| parseAutoStart((String) manifest.get(EclipseAdaptor.ECLIPSE_AUTOSTART)); |
| } |
| |
| public String getPluginClass() { |
| return pluginClass; |
| } |
| |
| public void setPluginClass(String value) { |
| pluginClass = value; |
| } |
| |
| public long getManifestTimeStamp() { |
| return manifestTimeStamp; |
| } |
| |
| public void setManifestTimeStamp(long stamp) { |
| manifestTimeStamp = stamp; |
| } |
| |
| public byte getManifestType() { |
| return manifestType; |
| } |
| |
| public void setManifestType(byte manifestType) { |
| this.manifestType = manifestType; |
| } |
| |
| public void setAutoStart(boolean value) { |
| autoStart = value; |
| } |
| |
| public boolean isAutoStart() { |
| return autoStart; |
| } |
| |
| public int getPersistentStatus() { |
| // omit the active state if necessary |
| return isAutoStartable() ? (~Constants.BUNDLE_STARTED) & getStatus() : getStatus(); |
| } |
| |
| public void setAutoStartExceptions(String[] autoStartExceptions) { |
| this.autoStartExceptions = autoStartExceptions; |
| } |
| |
| public String[] getAutoStartExceptions() { |
| return autoStartExceptions; |
| } |
| |
| private void parseAutoStart(String headerValue) { |
| autoStart = false; |
| autoStartExceptions = null; |
| ManifestElement[] allElements = null; |
| try { |
| allElements = ManifestElement.parseHeader(EclipseAdaptor.ECLIPSE_AUTOSTART, headerValue); |
| } catch (BundleException e) { |
| // just use the default settings (no auto activation) |
| String message = EclipseAdaptorMsg.formatter.getString("ECLIPSE_CLASSLOADER_CANNOT_GET_HEADERS", getLocation()); //$NON-NLS-1$ |
| EclipseAdaptor.getDefault().getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, message, 0, e, null)); |
| } |
| //Eclipse-AutoStart not found... |
| if (allElements == null) |
| return; |
| // the single value for this element should be true|false |
| autoStart = "true".equalsIgnoreCase(allElements[0].getValue()); //$NON-NLS-1$ |
| // look for any exceptions (the attribute) to the autoActivate setting |
| String exceptionsValue = allElements[0].getAttribute(EclipseAdaptor.ECLIPSE_AUTOSTART_EXCEPTIONS); |
| if (exceptionsValue == null) |
| return; |
| StringTokenizer tokenizer = new StringTokenizer(exceptionsValue, ","); //$NON-NLS-1$ |
| int numberOfTokens = tokenizer.countTokens(); |
| autoStartExceptions = new String[numberOfTokens]; |
| for (int i = 0; i < numberOfTokens; i++) |
| autoStartExceptions[i] = tokenizer.nextToken().trim(); |
| } |
| |
| public boolean isAutoStartable() { |
| return autoStart || (autoStartExceptions != null && autoStartExceptions.length > 0); |
| } |
| } |