blob: ec5adec42335798ce6b72b61e5ddc3e939655218 [file] [log] [blame]
/*******************************************************************************
* 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.osgi.framework.internal.defaultadaptor;
import java.io.*;
import org.eclipse.osgi.framework.adaptor.core.*;
import org.eclipse.osgi.framework.adaptor.core.AbstractBundleData;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.service.resolver.Version;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.*;
/**
* The <code>BundleData</code> represents a single bundle that is persistently
* stored by a <code>FrameworkAdaptor</code>. A <code>BundleData</code> creates
* the ClassLoader for a bundle, finds native libraries installed in the
* FrameworkAdaptor for the bundle, creates data files for the bundle,
* used to access bundle entries, manifest information, and getting and saving
* metadata.
*
*/
public class DefaultBundleData extends AbstractBundleData implements Cloneable {
/** The top level storage directory for the BundleData */
protected File bundleStoreDir;
/** The BundleData data directory */
protected File dirData;
/** The base BundleFile object for this BundleData */
protected BundleFile baseBundleFile;
/**
* Read data from an existing directory.
* This constructor is used by getInstalledBundles.
*
* @param directory The top level bundle directory.
* @throws NumberFormatException if the directory is not a
* number, the directory contains a ".delete" file or
* the directory does not contain a ".bundle" file.
* @throws IOException If an error occurs initializing the bundle data.
*/
public DefaultBundleData(DefaultAdaptor adaptor, long id) {
super(adaptor, id);
initBundleStoreDirs(String.valueOf(id));
}
/**
* Read data from an existing directory.
* This constructor is used by getInstalledBundles.
*
* @throws NumberFormatException if the directory is not a
* number, the directory contains a ".delete" file or
* the directory does not contain a ".bundle" file.
* @throws IOException If an error occurs initializing the bundle data.
*/
public void initializeExistingBundle() throws IOException {
File delete = new File(getBundleStoreDir(), ".delete");
/* and the directory is not marked for delete */
if (delete.exists())
throw new IOException();
createBaseBundleFile();
loadFromManifest();
}
public void initializeNewBundle() throws IOException {
createBaseBundleFile();
loadFromManifest();
}
protected void initBundleStoreDirs(String bundleID) {
setBundleStoreDir(new File(((DefaultAdaptor) adaptor).getBundleStoreRootDir(), bundleID));
}
/**
* Returns the absolute path name of a native library. The BundleData
* ClassLoader invokes this method to locate the native libraries that
* belong to classes loaded from this BundleData. Returns
* null if the library does not exist in this BundleData.
* @param libname The name of the library to find the absolute path to.
* @return The absolute path name of the native library or null if
* the library does not exist.
*/
public String findLibrary(String libname) {
String mappedName = System.mapLibraryName(libname);
String path = null;
if (Debug.DEBUG && Debug.DEBUG_LOADER) {
Debug.println(" mapped library name: " + mappedName);
}
path = findNativePath(mappedName);
if (path == null) {
if (Debug.DEBUG && Debug.DEBUG_LOADER) {
Debug.println(" library does not exist: " + mappedName);
}
path = findNativePath(libname);
}
if (Debug.DEBUG && Debug.DEBUG_LOADER) {
Debug.println(" returning library: " + path);
}
return path;
}
protected String findNativePath(String libname) {
String path = null;
if (!libname.startsWith("/")) {
libname = '/' + libname;
}
String[] nativepaths = getNativePaths();
if (nativepaths != null) {
for (int i = 0; i < nativepaths.length; i++) {
if (nativepaths[i].endsWith(libname)) {
File nativeFile = baseBundleFile.getFile(nativepaths[i]);
path = nativeFile.getAbsolutePath();
}
}
}
return path;
}
/**
* Installs the native code paths for this BundleData. Each
* element of nativepaths must be installed for lookup when findLibrary
* is called.
* @param nativepaths The array of native code paths to install for
* the bundle.
* @throws BundleException If any error occurs during install.
*/
public void installNativeCode(String[] nativepaths) throws BundleException {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < nativepaths.length; i++) {
// extract the native code
File nativeFile = baseBundleFile.getFile(nativepaths[i]);
if (nativeFile == null) {
throw new BundleException(AdaptorMsg.formatter.getString("BUNDLE_NATIVECODE_EXCEPTION", nativepaths[i])); //$NON-NLS-1$
}
sb.append(nativepaths[i]);
if (i < nativepaths.length - 1) {
sb.append(",");
}
}
if (sb.length() > 0)
setNativePaths(sb.toString());
}
protected void setDataDir(File dirData) {
this.dirData = dirData;
}
protected File getDataDir() {
return dirData;
}
/**
* Return the bundle data directory.
* Attempt to create the directory if it does not exist.
*
* @return Bundle data directory.
*/
public File getDataFile(String path) {
// lazily initialize dirData to prevent early access to instance location
if (getDataDir() == null) {
File dataRoot = ((DefaultAdaptor) adaptor).getDataRootDir();
if (dataRoot == null)
throw new IllegalStateException(AdaptorMsg.formatter.getString("ADAPTOR_DATA_AREA_NOT_SET")); //$NON-NLS-1$
setDataDir(new File(dataRoot, id + "/" + DefaultAdaptor.DATA_DIR_NAME));
}
if (!getDataDir().exists() && !getDataDir().mkdirs()) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Unable to create bundle data directory: " + getDataDir().getPath());
}
}
return (new File(getDataDir(), path));
}
/**
* Close all resources for this BundleData
*/
public void close() throws IOException {
if (baseBundleFile != null) {
baseBundleFile.close();
}
}
/**
* Opens all resource for this BundleData. Reopens the BundleData if
* it was previosly closed.
*/
public void open() throws IOException {
baseBundleFile.open();
}
protected void loadFromManifest() throws IOException {
try {
getManifest();
} catch (BundleException e) {
throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_ERROR_GETTING_MANIFEST", getLocation())); //$NON-NLS-1$
}
if (manifest == null) {
throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_ERROR_GETTING_MANIFEST", getLocation())); //$NON-NLS-1$
}
setVersion(new Version((String) manifest.get(Constants.BUNDLE_VERSION)));
String symbolicNameHeader = (String) manifest.get(Constants.BUNDLE_SYMBOLICNAME);
if (symbolicNameHeader != null)
try {
setSymbolicName(ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, symbolicNameHeader)[0].getValue());
} catch (BundleException e) {
// not doing validation here - just ignore
}
setClassPath((String) manifest.get(Constants.BUNDLE_CLASSPATH));
setActivator((String) manifest.get(Constants.BUNDLE_ACTIVATOR));
String host = (String) manifest.get(Constants.FRAGMENT_HOST);
setFragment(host != null);
setExecutionEnvironment((String) manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
setDynamicImports((String) manifest.get(Constants.DYNAMICIMPORT_PACKAGE));
}
protected File getGenerationDir() {
return new File(getBundleStoreDir(), String.valueOf(getGeneration()));
}
/**
* Return the bundle generation directory.
* Attempt to create the directory if it does not exist.
*
* @return Bundle generation directory.
*/
public File createGenerationDir() {
File generationDir = getGenerationDir();
if (!generationDir.exists() && !generationDir.mkdirs()) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Unable to create bundle generation directory: " + generationDir.getPath());
}
}
return generationDir;
}
/**
* Return the base File for the bundle.
* Attempt to create the bundle generation directory if it does not exist.
*
* @return the base File object for the bundle.
*/
protected File getBaseFile() {
return isReference() ? new File(getFileName()) : new File(createGenerationDir(), getFileName());
}
protected File getBundleStoreDir() {
return bundleStoreDir;
}
protected void setBundleStoreDir(File bundleStoreDir) {
this.bundleStoreDir = bundleStoreDir;
}
/**
* Return the top level bundle directory.
*
* @return Top level bundle directory.
*/
protected File createBundleStoreDir() {
if (!getBundleStoreDir().exists() && !getBundleStoreDir().mkdirs()) {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("Unable to create bundle store directory: " + getBundleStoreDir().getPath());
}
}
return getBundleStoreDir();
}
/**
* Save the bundle data in the data file.
*
* @throws IOException if a write error occurs.
*/
public synchronized void save() throws IOException {
((DefaultAdaptor) adaptor).saveMetaDataFor(this);
}
/**
* Return a copy of this object with the
* generation dependent fields updated to
* the next free generation level.
*
* @throws IOException If there are no more available generation levels.
*/
protected DefaultBundleData nextGeneration(String referenceFile) throws IOException {
int nextGeneration = getGeneration();
while (nextGeneration < Integer.MAX_VALUE) {
nextGeneration++;
File nextDirGeneration = new File(getBundleStoreDir(), String.valueOf(nextGeneration));
if (nextDirGeneration.exists()) {
continue;
}
DefaultBundleData next;
try {
next = (DefaultBundleData) clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
next.setGeneration(nextGeneration);
if (referenceFile != null) {
next.setReference(true);
next.setFileName(referenceFile);
} else {
if (next.isReference()) {
next.setReference(false);
next.setFileName(((DefaultAdaptor) adaptor).mapLocationToName(getLocation()));
}
}
// null out the manifest to force it to be re-read.
next.manifest = null;
return (next);
}
throw new IOException(AdaptorMsg.formatter.getString("ADAPTOR_STORAGE_EXCEPTION")); //$NON-NLS-1$
}
protected BundleFile createBaseBundleFile() throws IOException {
baseBundleFile = BundleFile.createBundleFile(getBaseFile(), this);
return baseBundleFile;
}
public BundleFile getBaseBundleFile() {
return baseBundleFile;
}
public String toString() {
return getLocation();
}
}