Modified the Felix bundle cache to support installing bundles by referenced
JAR files as well as exploded directories. These changes percolated into
various other areaas of the impl which are also changed here.
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@380806 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleInfo.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleInfo.java
index 1d059a5..3ffcdd4 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleInfo.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleInfo.java
@@ -18,14 +18,14 @@
import java.util.Map;
-import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.cache.DefaultBundleArchive;
import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.*;
class BundleInfo
{
private Logger m_logger = null;
- private BundleArchive m_archive = null;
+ private DefaultBundleArchive m_archive = null;
private IModule[] m_modules = null;
private int m_state = 0;
private long m_modified = 0;
@@ -39,7 +39,7 @@
private int m_lockCount = 0;
private Thread m_lockThread = null;
- protected BundleInfo(Logger logger, BundleArchive archive, IModule module)
+ protected BundleInfo(Logger logger, DefaultBundleArchive archive, IModule module)
throws Exception
{
m_logger = logger;
@@ -56,7 +56,7 @@
* Returns the bundle archive associated with this bundle.
* @return the bundle archive associated with this bundle.
**/
- public BundleArchive getArchive()
+ public DefaultBundleArchive getArchive()
{
return m_archive;
}
@@ -121,7 +121,18 @@
public long getBundleId()
{
- return m_archive.getId();
+ try
+ {
+ return m_archive.getId();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error getting the identifier from bundle archive.",
+ ex);
+ return -1;
+ }
}
public String getLocation()
@@ -134,7 +145,7 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "Error reading location from bundle archive.",
+ "Error getting location from bundle archive.",
ex);
return null;
}
@@ -177,7 +188,7 @@
{
// Return the header for the most recent bundle revision only,
// since we shouldn't ever need access to older revisions.
- return m_archive.getManifestHeader(m_archive.getRevisionCount() - 1);
+ return m_archive.getRevision(m_archive.getRevisionCount() - 1).getManifestHeader();
}
catch (Exception ex)
{
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
index e6e4d0b..977d2a3 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -80,7 +80,7 @@
FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
// Local file system cache.
- private BundleCache m_cache = null;
+ private DefaultBundleCache m_cache = null;
// Next available bundle identifier.
private long m_nextId = 1L;
@@ -240,19 +240,9 @@
}
});
- // Create default storage system from the specified cache class
- // or use the default cache if no custom cache was specified.
- String className = m_config.get(FelixConstants.CACHE_CLASS_PROP);
- if (className == null)
- {
- className = DefaultBundleCache.class.getName();
- }
-
try
{
- Class clazz = Class.forName(className);
- m_cache = (BundleCache) clazz.newInstance();
- m_cache.initialize(m_config, m_logger);
+ m_cache = new DefaultBundleCache(m_config, m_logger);
}
catch (Exception ex)
{
@@ -389,7 +379,7 @@
}
// Reload and cached bundles.
- BundleArchive[] archives = null;
+ DefaultBundleArchive[] archives = null;
// First get cached bundle identifiers.
try
@@ -409,13 +399,13 @@
// Now install all cached bundles.
for (int i = 0; (archives != null) && (i < archives.length); i++)
{
- // Make sure our id generator is not going to overlap.
- // TODO: This is not correct since it may lead to re-used
- // ids, which is not okay according to OSGi.
- m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
-
try
{
+ // Make sure our id generator is not going to overlap.
+ // TODO: This is not correct since it may lead to re-used
+ // ids, which is not okay according to OSGi.
+ m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
+
// It is possible that a bundle in the cache was previously
// uninstalled, but not completely deleted (perhaps because
// of a crash or a locked file), so if we see an archive
@@ -447,7 +437,7 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "Unable to re-install bundle " + archives[i].getId(),
+ "Unable to re-install cached bundle.",
ex);
}
// TODO: Perhaps we should remove the cached bundle?
@@ -1086,7 +1076,7 @@
}
}
// Strip leading '/' if present.
- if (path.charAt(0) == '/')
+ if ((path.length() > 0) && (path.charAt(0) == '/'))
{
path = path.substring(1);
}
@@ -1534,34 +1524,11 @@
try
{
- // Get the URL input stream if necessary.
- if (is == null)
- {
- // Do it the manual way to have a chance to
- // set request properties such as proxy auth.
- URL url = new URL(updateLocation);
- URLConnection conn = url.openConnection();
-
- // Support for http proxy authentication.
- String auth = System.getProperty("http.proxyAuth");
- if ((auth != null) && (auth.length() > 0))
- {
- if ("http".equals(url.getProtocol()) ||
- "https".equals(url.getProtocol()))
- {
- String base64 = Util.base64Encode(auth);
- conn.setRequestProperty(
- "Proxy-Authorization", "Basic " + base64);
- }
- }
- is = conn.getInputStream();
- }
-
// Get the bundle's archive.
- BundleArchive archive = m_cache.getArchive(info.getBundleId());
+ DefaultBundleArchive archive = m_cache.getArchive(info.getBundleId());
// Update the bundle; this operation will increase
// the revision count for the bundle.
- m_cache.update(archive, is);
+ archive.revise(updateLocation, is);
// Create a module for the new revision; the revision is
// base zero, so subtract one from the revision count to
// get the revision of the new update.
@@ -1594,7 +1561,7 @@
// This will not start the bundle if it was not previously
// active.
startBundle(bundle, false);
-
+
// If update failed, rethrow exception.
if (rethrow != null)
{
@@ -1860,7 +1827,7 @@
{
AccessController.checkPermission(m_adminPerm);
}
-
+
BundleImpl bundle = null;
// Acquire an install lock.
@@ -1894,30 +1861,8 @@
try
{
- // Get the URL input stream if necessary.
- if (is == null)
- {
- // Do it the manual way to have a chance to
- // set request properties such as proxy auth.
- URL url = new URL(location);
- URLConnection conn = url.openConnection();
-
- // Support for http proxy authentication.
- String auth = System.getProperty("http.proxyAuth");
- if ((auth != null) && (auth.length() > 0))
- {
- if ("http".equals(url.getProtocol()) ||
- "https".equals(url.getProtocol()))
- {
- String base64 = Util.base64Encode(auth);
- conn.setRequestProperty(
- "Proxy-Authorization", "Basic " + base64);
- }
- }
- is = conn.getInputStream();
- }
// Add the bundle to the cache.
- m_cache.create(id, location, is);
+ m_cache.create(id, location);
}
catch (Exception ex)
{
@@ -1949,7 +1894,7 @@
{
if (m_cache.getArchive(id).getRevisionCount() > 1)
{
- m_cache.purge(m_cache.getArchive(id));
+ m_cache.getArchive(id).purge();
}
}
catch (Exception ex)
@@ -1962,7 +1907,7 @@
try
{
- BundleArchive archive = m_cache.getArchive(id);
+ DefaultBundleArchive archive = m_cache.getArchive(id);
bundle = new BundleImpl(this, createBundleInfo(archive));
}
catch (Exception ex)
@@ -2884,7 +2829,7 @@
// Miscellaneous private methods.
//
- private BundleInfo createBundleInfo(BundleArchive archive)
+ private BundleInfo createBundleInfo(DefaultBundleArchive archive)
throws Exception
{
// Get the bundle manifest.
@@ -2893,7 +2838,7 @@
{
// Although there should only ever be one revision at this
// point, get the header for the current revision to be safe.
- headerMap = archive.getManifestHeader(archive.getRevisionCount() - 1);
+ headerMap = archive.getRevision(archive.getRevisionCount() - 1).getManifestHeader();
}
catch (Exception ex)
{
@@ -3142,8 +3087,8 @@
// Create the content loader associated with the module archive.
IContentLoader contentLoader = new ContentLoaderImpl(
m_logger,
- m_cache.getArchive(id).getContent(revision),
- m_cache.getArchive(id).getContentPath(revision));
+ m_cache.getArchive(id).getRevision(revision).getContent(),
+ m_cache.getArchive(id).getRevision(revision).getContentPath());
// Set the content loader's search policy.
contentLoader.setSearchPolicy(
new R4SearchPolicy(m_policyCore, module));
@@ -3190,11 +3135,11 @@
if (activator == null)
{
// Get the associated bundle archive.
- BundleArchive ba = m_cache.getArchive(info.getBundleId());
+ DefaultBundleArchive ba = m_cache.getArchive(info.getBundleId());
// Get the manifest from the current revision; revision is
// base zero so subtract one from the count to get the
// current revision.
- Map headerMap = ba.getManifestHeader(ba.getRevisionCount() - 1);
+ Map headerMap = ba.getRevision(ba.getRevisionCount() - 1).getManifestHeader();
// Get the activator class attribute.
String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
// Try to instantiate activator class if present.
@@ -3241,7 +3186,7 @@
}
// Purge all bundle revisions, but the current one.
- m_cache.purge(m_cache.getArchive(info.getBundleId()));
+ m_cache.getArchive(info.getBundleId()).purge();
}
finally
{
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundle.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundle.java
index deb331d..5053bdc 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundle.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundle.java
@@ -21,6 +21,7 @@
import java.security.PrivilegedAction;
import java.util.*;
+import org.apache.felix.framework.cache.SystemBundleArchive;
import org.apache.felix.framework.searchpolicy.*;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
deleted file mode 100644
index 58104c6..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2006 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-package org.apache.felix.framework;
-
-import java.io.File;
-import java.util.Map;
-
-import org.apache.felix.framework.cache.BundleArchive;
-import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.IModule;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleActivator;
-
-public class SystemBundleArchive implements BundleArchive
-{
- private Map m_headerMap = null;
-
- public long getId()
- {
- return 0;
- }
-
- public String getLocation()
- throws Exception
- {
- return FelixConstants.SYSTEM_BUNDLE_LOCATION;
- }
-
- public int getPersistentState()
- throws Exception
- {
- return Bundle.ACTIVE;
- }
-
- public void setPersistentState(int state)
- throws Exception
- {
- }
-
- public int getStartLevel()
- throws Exception
- {
- return FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
- }
-
- public void setStartLevel(int level)
- throws Exception
- {
- }
-
- public File getDataFile(String fileName)
- throws Exception
- {
- return null;
- }
-
- public BundleActivator getActivator(IModule module)
- throws Exception
- {
- return null;
- }
-
- public void setActivator(Object obj)
- throws Exception
- {
- }
-
- public int getRevisionCount()
- throws Exception
- {
- return 1;
- }
-
- public Map getManifestHeader(int revision)
- throws Exception
- {
- return m_headerMap;
- }
-
- protected void setManifestHeader(Map headerMap)
- {
- m_headerMap = headerMap;
- }
-
- public IContent getContent(int revision)
- throws Exception
- {
- return null;
- }
-
- public IContent[] getContentPath(int revision)
- throws Exception
- {
- return null;
- }
-
- public String findLibrary(int revision, String libName)
- throws Exception
- {
- return null;
- }
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
deleted file mode 100644
index cc032ce..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright 2006 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-package org.apache.felix.framework.cache;
-
-import java.io.File;
-import java.util.Map;
-
-import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.IModule;
-import org.osgi.framework.BundleActivator;
-
-/**
- * <p>
- * This interface represents an individual cached bundle in the
- * bundle cache. Felix uses this interface to access all information
- * about the associated bundle's cached information. Classes that implement
- * this interface will be related to a specific implementation of the
- * <tt>BundleCache</tt> interface.
- * </p>
- * @see org.apache.felix.framework.BundleCache
-**/
-public interface BundleArchive
-{
- /**
- * <p>
- * Returns the identifier of the bundle associated with this archive.
- * </p>
- * @return the identifier of the bundle associated with this archive.
- **/
- public long getId();
-
- /**
- * <p>
- * Returns the location string of the bundle associated with this archive.
- * </p>
- * @return the location string of the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public String getLocation()
- throws Exception;
-
- /**
- * <p>
- * Returns the persistent state of the bundle associated with the archive;
- * this value will be either <tt>Bundle.INSTALLED</tt> or <tt>Bundle.ACTIVE</tt>.
- * </p>
- * @return the persistent state of the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public int getPersistentState()
- throws Exception;
-
- /**
- * <p>
- * Sets the persistent state of the bundle associated with this archive;
- * this value will be either <tt>Bundle.INSTALLED</tt> or <tt>Bundle.ACTIVE</tt>.
- * </p>
- * @param state the new bundle state to write to the archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public void setPersistentState(int state)
- throws Exception;
-
- /**
- * <p>
- * Returns the start level of the bundle associated with this archive.
- * </p>
- * @return the start level of the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public int getStartLevel()
- throws Exception;
-
- /**
- * <p>
- * Sets the start level of the bundle associated with this archive.
- * </p>
- * @param level the new bundle start level to write to the archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public void setStartLevel(int level)
- throws Exception;
-
- /**
- * <p>
- * Returns an appropriate data file for the bundle associated with the
- * archive using the supplied file name.
- * </p>
- * @return a <tt>File</tt> corresponding to the requested data file for
- * the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public File getDataFile(String fileName)
- throws Exception;
-
- /**
- * <p>
- * Returns the persistent bundle activator of the bundle associated with
- * this archive; this is a non-standard OSGi method that is only called
- * when Felix is running in non-strict OSGi mode.
- * </p>
- * @param module the module to use when trying to instantiate
- * the bundle activator.
- * @return the persistent bundle activator of the bundle associated with
- * this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public BundleActivator getActivator(IModule module)
- throws Exception;
-
- /**
- * <p>
- * Sets the persistent bundle activator of the bundle associated with
- * this archive; this is a non-standard OSGi method that is only called
- * when Felix is running in non-strict OSGi mode.
- * </p>
- * @param obj the new persistent bundle activator to write to the archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public void setActivator(Object obj)
- throws Exception;
-
- /**
- * <p>
- * Returns the number of revisions of the bundle associated with the
- * archive. When a bundle is updated, the previous version of the bundle
- * is maintained along with the new revision until old revisions are
- * purged. The revision count reflects how many revisions of the bundle
- * are currently available in the cache.
- * </p>
- * @return the number of revisions of the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public int getRevisionCount()
- throws Exception;
-
- /**
- * <p>
- * Returns the main attributes of the JAR file manifest header of the
- * specified revision of the bundle associated with this archive. The
- * returned map should be case insensitive.
- * </p>
- * @param revision the specified revision.
- * @return the case-insensitive JAR file manifest header of the specified
- * revision of the bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public Map getManifestHeader(int revision)
- throws Exception;
-
- /**
- * <p>
- * Returns a content object that is associated with the specified bundle
- * revision's JAR file.
- * </p>
- * @param revision the specified revision.
- * @return A content object for the specified bundle revision's JAR file.
- * @throws java.lang.Exception if any error occurs.
- **/
- public IContent getContent(int revision)
- throws Exception;
-
- /**
- * <p>
- * Returns an array of content objects that are associated with the
- * specified bundle revision's class path.
- * </p>
- * @param revision the specified revision.
- * @return An array of content objects for the specified bundle revision's
- * class path.
- * @throws java.lang.Exception if any error occurs.
- **/
- public IContent[] getContentPath(int revision)
- throws Exception;
-
- /**
- * <p>
- * Returns the absolute file path for the specified native library of the
- * specified revision of the bundle associated with this archive.
- * </p>
- * @param revision the specified revision.
- * @param libName the name of the library.
- * @return a <tt>String</tt> that contains the absolute path name to
- * the requested native library of the specified revision of the
- * bundle associated with this archive.
- * @throws java.lang.Exception if any error occurs.
- **/
- public String findLibrary(int revision, String libName)
- throws Exception;
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
deleted file mode 100644
index a1ddc76..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2005 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-package org.apache.felix.framework.cache;
-
-import java.io.InputStream;
-
-import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.PropertyResolver;
-
-/**
- * <p>
- * This interface represents the storage mechanism that Felix uses for
- * caching bundles. It is possible for multiple implementations of
- * this interface to exist for different storage technologies, such as the
- * file system, memory, or a database. Felix includes a default implementation
- * of this interface that uses the file system. Felix allows you to specify
- * alternative implementations to use by specifying a class name via the
- * <tt>felix.cache.class</tt> system property. Bundle cache implemenations
- * should implement this interface and provide a default constructor.
- * </p>
- * @see org.apache.felix.framework.BundleArchive
-**/
-public interface BundleCache
-{
- /**
- * <p>
- * This method is called before using the BundleCache implementation
- * to initialize it and to pass it a reference to its associated
- * configuration property resolver and logger. The <tt>BundleCache</tt>
- * implementation should not use <tt>System.getProperty()</tt> directly
- * for configuration properties, it should use the property resolver
- * instance passed into this method. The property resolver
- * provides access to properties passed into the Felix instance's
- * constructor. This approach allows multiple instances of Felix to
- * exist in memory at the same time, but for
- * them to be configured differently. For example, an application may
- * want two instances of Felix, where each instance stores their cache
- * in a different location in the file system. When using multiple
- * instances of Felix in memory at the same time, system properties
- * should be avoided and all properties should be passed in to Felix's
- * constructor.
- * </p>
- * @param cfg the property resolver for obtaining configuration properties.
- * @param logger the logger to use for reporting errors.
- * @throws Exception if any error occurs.
- **/
- public void initialize(PropertyResolver cfg, Logger logger)
- throws Exception;
-
- /**
- * <p>
- * Returns all cached bundle archives.
- * </p>
- * @return an array of all cached bundle archives.
- * @throws Exception if any error occurs.
- **/
- public BundleArchive[] getArchives()
- throws Exception;
-
- /**
- * <p>
- * Returns the bundle archive associated with the specified
- * bundle indentifier.
- * </p>
- * @param id the identifier of the bundle archive to retrieve.
- * @return the bundle archive assocaited with the specified bundle identifier.
- * @throws Exception if any error occurs.
- **/
- public BundleArchive getArchive(long id)
- throws Exception;
-
- /**
- * <p>
- * Creates a new bundle archive for the specified bundle
- * identifier using the supplied location string and input stream. The
- * contents of the bundle JAR file should be read from the supplied
- * input stream, which will not be <tt>null</tt>. The input stream is
- * closed by the caller; the implementation is only responsible for
- * closing streams it opens. If this method completes successfully, then
- * it means that the initial bundle revision of the specified bundle was
- * successfully cached.
- * </p>
- * @param id the identifier of the bundle associated with the new archive.
- * @param location the location of the bundle associated with the new archive.
- * @param is the input stream to the bundle's JAR file.
- * @return the created bundle archive.
- * @throws Exception if any error occurs.
- **/
- public BundleArchive create(long id, String location, InputStream is)
- throws Exception;
-
- /**
- * <p>
- * Saves an updated revision of the specified bundle to
- * the bundle cache using the supplied input stream. The contents of the
- * updated bundle JAR file should be read from the supplied input stream,
- * which will not be <tt>null</tt>. The input stream is closed by the
- * caller; the implementation is only responsible for closing streams
- * it opens. Updating a bundle in the cache does not replace the current
- * revision of the bundle, it makes a new revision available. If this
- * method completes successfully, then it means that the number of
- * revisions of the specified bundle has increased by one.
- * </p>
- * @param ba the bundle archive of the bundle to update.
- * @param is the input stream to the bundle's updated JAR file.
- * @throws Exception if any error occurs.
- **/
- public void update(BundleArchive ba, InputStream is)
- throws Exception;
-
- /**
- * <p>
- * Purges all old revisions of the specified bundle from
- * the cache. If this method completes successfully, then it means that
- * only the most current revision of the bundle should exist in the cache.
- * </p>
- * @param ba the bundle archive of the bundle to purge.
- * @throws Exception if any error occurs.
- **/
- public void purge(BundleArchive ba)
- throws Exception;
-
- /**
- * <p>
- * Removes the specified bundle from the cache. If this method
- * completes successfully, there should be no trace of the removed bundle
- * in the cache.
- * </p>
- * @param ba the bundle archive of the bundle to remove.
- * @throws Exception if any error occurs.
- **/
- public void remove(BundleArchive ba)
- throws Exception;
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
new file mode 100644
index 0000000..7607e5e
--- /dev/null
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.moduleloader.IContent;
+
+/**
+ * <p>
+ * This class implements an abstract revision of a bundle archive. A revision
+ * is an abstraction of a bundle's actual content and is associated with a
+ * parent bundle archive. A bundle archive may have multiple revisions assocaited
+ * with it at one time, since updating a bundle results in a new version of the
+ * bundle's content until the bundle is refreshed. Upon a refresh, then old
+ * revisions are then purged. This abstract class is the base class for all
+ * concrete types of revisions, such as ones for a JAR file or directories. All
+ * revisions are assigned a root directory into which all of their state should
+ * be stored, if necessary. Clean up of this directory is the responsibility
+ * of the parent bundle archive and not of the revision itself.
+ * </p>
+ * @see org.apache.felix.framework.cache.BundleCache
+ * @see org.apache.felix.framework.cache.BundleArchive
+**/
+public abstract class BundleRevision
+{
+ private Logger m_logger;
+ private File m_revisionRootDir = null;
+ private String m_location = null;
+
+ /**
+ * <p>
+ * This constructor is only used by the system bundle archive.
+ * </p>
+ **/
+ BundleRevision()
+ {
+ }
+
+ /**
+ * <p>
+ * This class is abstract and cannot be created. It represents a revision
+ * of a bundle, i.e., its content. A revision is associated with a particular
+ * location string, which is typically in URL format. Subclasses of this
+ * class provide particular functionality, such as a revision in the form
+ * of a JAR file or a directory. Each revision subclass is expected to use
+ * the root directory associated with the abstract revision instance to
+ * store any state; this will ensure that resources used by the revision are
+ * properly freed when the revision is no longer needed.
+ * </p>
+ * @param logger a logger for use by the revision.
+ * @param revisionRootDir the root directory to be used by the revision
+ * subclass for storing any state.
+ * @param location the location string associated with the revision.
+ * @throws Exception if any errors occur.
+ **/
+ public BundleRevision(Logger logger, File revisionRootDir, String location)
+ throws Exception
+ {
+ m_logger = logger;
+ m_revisionRootDir = revisionRootDir;
+ m_location = location;
+ }
+
+
+ /**
+ * <p>
+ * Returns the logger for this revision.
+ * <p>
+ * @return the logger instance for this revision.
+ **/
+ public Logger getLogger()
+ {
+ return m_logger;
+ }
+
+ /**
+ * <p>
+ * Returns the root directory for this revision.
+ * </p>
+ * @return the root directory for this revision.
+ **/
+ public File getRevisionRootDir()
+ {
+ return m_revisionRootDir;
+ }
+
+ /**
+ * <p>
+ * Returns the location string this revision.
+ * </p>
+ * @return the location string for this revision.
+ **/
+ public String getLocation()
+ {
+ return m_location;
+ }
+
+ /**
+ * <p>
+ * Returns the main attributes of the JAR file manifest header of the
+ * revision. The returned map is case insensitive.
+ * </p>
+ * @return the case-insensitive JAR file manifest header of the revision.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public abstract Map getManifestHeader() throws Exception;
+
+ /**
+ * <p>
+ * Returns a content object that is associated with the revision.
+ * </p>
+ * @return a content object that is associated with the revision.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public abstract IContent getContent() throws Exception;
+
+ /**
+ * <p>
+ * Returns an array of content objects that are associated with the
+ * specified revision's bundle class path.
+ * </p>
+ * @return an array of content objects for the revision's bundle class path.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public abstract IContent[] getContentPath() throws Exception;
+
+ /**
+ * <p>
+ * Returns the absolute file path for the specified native library of the
+ * revision.
+ * </p>
+ * @param libName the name of the library.
+ * @return a <tt>String</tt> that contains the absolute path name to
+ * the requested native library of the revision.
+ * @throws java.lang.Exception if any error occurs.
+ **/
+ public abstract String findLibrary(String libName) throws Exception;
+
+ /**
+ * <p>
+ * This method is called when the revision is no longer needed. The directory
+ * associated with the revision will automatically be removed for each
+ * revision, so this method only needs to be concerned with other issues,
+ * such as open files.
+ * </p>
+ * @throws Exception if any error occurs.
+ **/
+ public abstract void dispose() throws Exception;
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleArchive.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleArchive.java
index 893d9dc..2fcb2a4 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleArchive.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleArchive.java
@@ -17,268 +17,257 @@
package org.apache.felix.framework.cache;
import java.io.*;
-import java.security.*;
-import java.util.*;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.*;
-import org.apache.felix.moduleloader.*;
+import org.apache.felix.framework.util.ObjectInputStreamX;
+import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
/**
* <p>
- * This class, combined with <tt>DefaultBundleCache</tt>, implements the
- * default file system-based bundle cache for Felix.
+ * This class is a logical abstraction for a bundle archive. This class,
+ * combined with <tt>BundleCache</tt> and concrete <tt>BundleRevision</tt>
+ * subclasses, implement the bundle cache for Felix. The bundle archive
+ * abstracts the actual bundle content into revisions and the revisions
+ * provide access to the actual bundle content. When a bundle is
+ * installed it has one revision associated with its content. Updating a
+ * bundle adds another revision for the updated content. Any number of
+ * revisions can be associated with a bundle archive. When the bundle
+ * (or framework) is refreshed, then all old revisions are purged and only
+ * the most recent revision is maintained.
* </p>
- * @see org.apache.felix.framework.util.DefaultBundleCache
+ * <p>
+ * The content associated with a revision can come in many forms, such as
+ * a standard JAR file or an exploded bundle directory. The bundle archive
+ * is responsible for creating all revision instances during invocations
+ * of the <tt>revise()</tt> method call. Internally, it determines the
+ * concrete type of revision type by examining the location string as an
+ * URL. Currently, it supports standard JAR files, referenced JAR files,
+ * and referenced directories. Examples of each type of URL are, respectively:
+ * </p>
+ * <ul>
+ * <li><tt>http://www.foo.com/bundle.jar</tt></li>
+ * <li><tt>reference:file:/foo/bundle.jar</tt></li>
+ * <li><tt>reference:file:/foo/bundle/</tt></li>
+ * </ul>
+ * <p>
+ * The "<tt>reference:</tt>" notation signifies that the resource should be
+ * used "in place", meaning that they will not be copied. For referenced JAR
+ * files, some resources may still be copied, such as embedded JAR files or
+ * native libraries, but for referenced exploded bundle directories, nothing
+ * will be copied. Currently, reference URLs can only refer to "file:" targets.
+ * </p>
+ * @see org.apache.felix.framework.cache.BundleCache
+ * @see org.apache.felix.framework.cache.BundleRevision
**/
-public class DefaultBundleArchive implements BundleArchive
+public class DefaultBundleArchive
{
- private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
+ public static final transient String FILE_PROTOCOL = "file:";
+ public static final transient String REFERENCE_PROTOCOL = "reference:";
+ public static final transient String INPUTSTREAM_PROTOCOL = "inputstream:";
+
+ private static final transient String BUNDLE_ID_FILE = "bundle.id";
private static final transient String BUNDLE_LOCATION_FILE = "bundle.location";
+ private static final transient String CURRENT_LOCATION_FILE = "current.location";
private static final transient String BUNDLE_STATE_FILE = "bundle.state";
private static final transient String BUNDLE_START_LEVEL_FILE = "bundle.startlevel";
private static final transient String REFRESH_COUNTER_FILE = "refresh.counter";
private static final transient String BUNDLE_ACTIVATOR_FILE = "bundle.activator";
-
private static final transient String REVISION_DIRECTORY = "version";
- private static final transient String EMBEDDED_DIRECTORY = "embedded";
- private static final transient String LIBRARY_DIRECTORY = "lib";
private static final transient String DATA_DIRECTORY = "data";
-
private static final transient String ACTIVE_STATE = "active";
private static final transient String INSTALLED_STATE = "installed";
private static final transient String UNINSTALLED_STATE = "uninstalled";
private Logger m_logger = null;
private long m_id = -1;
- private File m_dir = null;
- private String m_location = null;
+ private File m_archiveRootDir = null;
+ private String m_originalLocation = null;
+ private String m_currentLocation = null;
private int m_persistentState = -1;
private int m_startLevel = -1;
- private Map m_currentHeader = null;
+ private BundleRevision[] m_revisions = null;
private long m_refreshCount = -1;
- private int m_revisionCount = -1;
- public DefaultBundleArchive(
- Logger logger, File dir, long id, String location, InputStream is)
- throws Exception
+ /**
+ * <p>
+ * This constructor is only used by the system bundle archive implementation
+ * because it is special an is not really an archive.
+ * </p>
+ **/
+ DefaultBundleArchive()
{
- this(logger, dir, id);
- m_location = location;
-
- // Try to save and pre-process the bundle JAR.
- try
- {
- initialize(is);
- }
- catch (Exception ex)
- {
- if (!deleteDirectoryTree(dir))
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "Unable to delete the archive directory: " + id);
- }
- throw ex;
- }
}
- public DefaultBundleArchive(Logger logger, File dir, long id)
+ /**
+ * <p>
+ * This constructor is used for creating new archives when a bundle is
+ * installed into the framework. Each archive receives a logger, a root
+ * directory, its associated bundle identifier, and the associated bundle
+ * location string. The root directory is where any required state can be
+ * stored.
+ * </p>
+ * @param logger the logger to be used by the archive.
+ * @param archiveRootDir the archive root directory for storing state.
+ * @param id the bundle identifier associated with the archive.
+ * @param location the bundle location string associated with the archive.
+ * @throws Exception if any error occurs.
+ **/
+ public DefaultBundleArchive(
+ Logger logger, File archiveRootDir, long id, String location)
+ throws Exception
{
m_logger = logger;
- m_dir = dir;
+ m_archiveRootDir = archiveRootDir;
m_id = id;
if (m_id <= 0)
{
throw new IllegalArgumentException(
"Bundle ID cannot be less than or equal to zero.");
}
+ m_originalLocation = location;
+
+ // Save state.
+ initialize();
+
+ // Add a revision for the content.
+ revise(getCurrentLocation(), null);
}
- private void initialize(InputStream is)
+ /**
+ * <p>
+ * This constructor is called when an archive for a bundle is being
+ * reconstructed when the framework is restarted. Each archive receives
+ * a logger, a root directory, and its associated bundle identifier.
+ * The root directory is where any required state can be stored.
+ * </p>
+ * @param logger the logger to be used by the archive.
+ * @param archiveRootDir the archive root directory for storing state.
+ * @param id the bundle identifier associated with the archive.
+ * @throws Exception if any error occurs.
+ **/
+ public DefaultBundleArchive(Logger logger, File archiveRootDir)
throws Exception
{
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.INITIALIZE_ACTION, this, is));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- initializeUnchecked(is);
- }
+ m_logger = logger;
+ m_archiveRootDir = archiveRootDir;
+
+ // Add a revision for the content.
+ revise(getCurrentLocation(), null);
}
- private void initializeUnchecked(InputStream is)
- throws Exception
+ /**
+ * <p>
+ * Returns the bundle identifier associated with this archive.
+ * </p>
+ * @return the bundle identifier associated with this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized long getId() throws Exception
{
- FileWriter fw = null;
- BufferedWriter bw = null;
-
- try
+ if (m_id > 0)
{
- // Create archive directory.
- if (!m_dir.mkdir())
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to create archive directory.");
- throw new IOException("Unable to create archive directory.");
- }
-
- // Save location string.
- File file = new File(m_dir, BUNDLE_LOCATION_FILE);
- fw = new FileWriter(file);
- bw = new BufferedWriter(fw);
- bw.write(m_location, 0, m_location.length());
-
- // Create version/revision directory for bundle JAR.
- // Since this is only called when the bundle JAR is
- // first saved, the update and revision will always
- // be "0.0" for the directory name.
- File revisionDir = new File(m_dir, REVISION_DIRECTORY + "0.0");
- if (!revisionDir.mkdir())
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to create revision directory.");
- throw new IOException("Unable to create revision directory.");
- }
-
- // Save the bundle jar file.
- file = new File(revisionDir, BUNDLE_JAR_FILE);
- copy(is, file);
-
- // This will always be revision zero.
- preprocessBundleJar(0, revisionDir);
-
+ return m_id;
}
- finally
- {
- if (is != null) is.close();
- if (bw != null) bw.close();
- if (fw != null) fw.close();
- }
- }
-
- public File getDirectory()
- {
- return m_dir;
- }
-
- public long getId()
- {
- return m_id;
- }
-
- public String getLocation()
- throws Exception
- {
- if (m_location != null)
- {
- return m_location;
- }
- else if (System.getSecurityManager() != null)
- {
- try
- {
- return (String) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_LOCATION_ACTION, this));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- return getLocationUnchecked();
- }
- }
-
- private String getLocationUnchecked()
- throws Exception
- {
- // Get bundle location file.
- File locFile = new File(m_dir, BUNDLE_LOCATION_FILE);
// Read bundle location.
- FileReader fr = null;
+ InputStream is = null;
BufferedReader br = null;
try
{
- fr = new FileReader(locFile);
- br = new BufferedReader(fr);
- m_location = br.readLine();
- return m_location;
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(new File(m_archiveRootDir, BUNDLE_ID_FILE));
+ br = new BufferedReader(new InputStreamReader(is));
+ m_id = Long.parseLong(br.readLine());
+ }
+ catch (FileNotFoundException ex)
+ {
+ // HACK: Get the bundle identifier from the archive root directory
+ // name, which is of the form "bundle<id>" where <id> is the bundle
+ // identifier numbers. This is a hack to deal with old archives that
+ // did not save their bundle identifier, but instead had it passed
+ // into them. Eventually, this can be removed.
+ m_id = Long.parseLong(
+ m_archiveRootDir.getName().substring(
+ DefaultBundleCache.BUNDLE_DIR_PREFIX.length()));
}
finally
{
if (br != null) br.close();
- if (fr != null) fr.close();
+ if (is != null) is.close();
+ }
+
+ return m_id;
+ }
+
+ /**
+ * <p>
+ * Returns the location string associated with this archive.
+ * </p>
+ * @return the location string associated with this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized String getLocation() throws Exception
+ {
+ if (m_originalLocation != null)
+ {
+ return m_originalLocation;
+ }
+
+ // Read bundle location.
+ InputStream is = null;
+ BufferedReader br = null;
+ try
+ {
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(new File(m_archiveRootDir, BUNDLE_LOCATION_FILE));
+ br = new BufferedReader(new InputStreamReader(is));
+ m_originalLocation = br.readLine();
+ return m_originalLocation;
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (is != null) is.close();
}
}
- public int getPersistentState()
- throws Exception
+ /**
+ * <p>
+ * Returns the persistent state of this archive. The value returned is
+ * one of the following: <tt>Bundle.INSTALLED</tt>, <tt>Bundle.ACTIVE</tt>,
+ * or <tt>Bundle.UNINSTALLED</tt>.
+ * </p>
+ * @return the persistent state of this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized int getPersistentState() throws Exception
{
if (m_persistentState >= 0)
{
return m_persistentState;
}
- else if (System.getSecurityManager() != null)
- {
- try
- {
- return ((Integer) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_PERSISTENT_STATE_ACTION, this))).intValue();
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- return getPersistentStateUnchecked();
- }
- }
- private int getPersistentStateUnchecked()
- throws Exception
- {
// Get bundle state file.
- File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
+ File stateFile = new File(m_archiveRootDir, BUNDLE_STATE_FILE);
// If the state file doesn't exist, then
// assume the bundle was installed.
- if (!stateFile.exists())
+ if (!DefaultBundleCache.getSecureAction().fileExists(stateFile))
{
return Bundle.INSTALLED;
}
// Read the bundle state.
- FileReader fr = null;
- BufferedReader br= null;
+ InputStream is = null;
+ BufferedReader br = null;
try
{
- fr = new FileReader(stateFile);
- br = new BufferedReader(fr);
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(stateFile);
+ br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();
if (s.equals(ACTIVE_STATE))
{
@@ -297,45 +286,29 @@
finally
{
if (br != null) br.close();
- if (fr != null) fr.close();
+ if (is != null) is.close();
}
}
- public void setPersistentState(int state)
- throws Exception
+ /**
+ * <p>
+ * Sets the persistent state of this archive. The value is
+ * one of the following: <tt>Bundle.INSTALLED</tt>, <tt>Bundle.ACTIVE</tt>,
+ * or <tt>Bundle.UNINSTALLED</tt>.
+ * </p>
+ * @param state the persistent state value to set for this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized void setPersistentState(int state) throws Exception
{
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.SET_PERSISTENT_STATE_ACTION, this, state));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- setPersistentStateUnchecked(state);
- }
- }
-
- private void setPersistentStateUnchecked(int state)
- throws Exception
- {
- // Get bundle state file.
- File stateFile = new File(m_dir, BUNDLE_STATE_FILE);
-
// Write the bundle state.
- FileWriter fw = null;
+ OutputStream os = null;
BufferedWriter bw= null;
try
{
- fw = new FileWriter(stateFile);
- bw = new BufferedWriter(fw);
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_STATE_FILE));
+ bw = new BufferedWriter(new OutputStreamWriter(os));
String s = null;
switch (state)
{
@@ -356,107 +329,75 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to record state: " + ex);
+ getClass().getName() + ": Unable to record state - " + ex);
throw ex;
}
finally
{
if (bw != null) bw.close();
- if (fw != null) fw.close();
+ if (os != null) os.close();
}
}
- public int getStartLevel()
- throws Exception
+ /**
+ * <p>
+ * Returns the start level of this archive.
+ * </p>
+ * @return the start level of this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized int getStartLevel() throws Exception
{
if (m_startLevel >= 0)
{
return m_startLevel;
}
- else if (System.getSecurityManager() != null)
- {
- try
- {
- return ((Integer) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_START_LEVEL_ACTION, this))).intValue();
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- return getStartLevelUnchecked();
- }
- }
- private int getStartLevelUnchecked()
- throws Exception
- {
// Get bundle start level file.
- File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
+ File levelFile = new File(m_archiveRootDir, BUNDLE_START_LEVEL_FILE);
// If the start level file doesn't exist, then
// return an error.
- if (!levelFile.exists())
+ if (!DefaultBundleCache.getSecureAction().fileExists(levelFile))
{
return -1;
}
// Read the bundle start level.
- FileReader fr = null;
+ InputStream is = null;
BufferedReader br= null;
try
{
- fr = new FileReader(levelFile);
- br = new BufferedReader(fr);
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(levelFile);
+ br = new BufferedReader(new InputStreamReader(is));
m_startLevel = Integer.parseInt(br.readLine());
return m_startLevel;
}
finally
{
if (br != null) br.close();
- if (fr != null) fr.close();
+ if (is != null) is.close();
}
}
- public void setStartLevel(int level)
- throws Exception
+ /**
+ * <p>
+ * Sets the the start level of this archive this archive.
+ * </p>
+ * @param level the start level to set for this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized void setStartLevel(int level) throws Exception
{
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.SET_START_LEVEL_ACTION, this, level));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- setStartLevelUnchecked(level);
- }
- }
-
- private void setStartLevelUnchecked(int level)
- throws Exception
- {
- // Get bundle start level file.
- File levelFile = new File(m_dir, BUNDLE_START_LEVEL_FILE);
-
// Write the bundle start level.
- FileWriter fw = null;
+ OutputStream os = null;
BufferedWriter bw = null;
try
{
- fw = new FileWriter(levelFile);
- bw = new BufferedWriter(fw);
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_START_LEVEL_FILE));
+ bw = new BufferedWriter(new OutputStreamWriter(os));
String s = Integer.toString(level);
bw.write(s, 0, s.length());
m_startLevel = level;
@@ -465,18 +406,25 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to record start leel: " + ex);
+ getClass().getName() + ": Unable to record start level - " + ex);
throw ex;
}
finally
{
if (bw != null) bw.close();
- if (fw != null) fw.close();
+ if (os != null) os.close();
}
}
- public File getDataFile(String fileName)
- throws Exception
+ /**
+ * <p>
+ * Returns a <tt>File</tt> object corresponding to the data file
+ * of the relative path of the specified string.
+ * </p>
+ * @return a <tt>File</tt> object corresponding to the specified file name.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized File getDataFile(String fileName) throws Exception
{
// Do some sanity checking.
if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
@@ -485,81 +433,47 @@
throw new IllegalArgumentException("The data file path cannot contain a reference to the \"..\" directory.");
// Get bundle data directory.
- File dataDir = new File(m_dir, DATA_DIRECTORY);
-
- if (System.getSecurityManager() != null)
+ File dataDir = new File(m_archiveRootDir, DATA_DIRECTORY);
+ // Create the data directory if necessary.
+ if (DefaultBundleCache.getSecureAction().fileExists(dataDir))
{
- try
+ if (!DefaultBundleCache.getSecureAction().mkdir(dataDir))
{
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.CREATE_DATA_DIR_ACTION, this, dataDir));
+ throw new IOException("Unable to create bundle data directory.");
}
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- createDataDirectoryUnchecked(dataDir);
}
// Return the data file.
return new File(dataDir, fileName);
}
- private void createDataDirectoryUnchecked(File dir)
- throws Exception
- {
- // Create data directory if necessary.
- if (!dir.exists())
- {
- if (!dir.mkdir())
- {
- throw new IOException("Unable to create bundle data directory.");
- }
- }
- }
-
- public BundleActivator getActivator(IModule module)
- throws Exception
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- return (BundleActivator) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_ACTIVATOR_ACTION, this, module));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- return getActivatorUnchecked(module);
- }
- }
-
- private BundleActivator getActivatorUnchecked(IModule module)
+ /**
+ * <p>
+ * Returns the serialized activator for this archive. This is an
+ * extension to the OSGi specification.
+ * </p>
+ * @return the serialized activator for this archive.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized BundleActivator getActivator(IModule module)
throws Exception
{
// Get bundle activator file.
- File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
+ File activatorFile = new File(m_archiveRootDir, BUNDLE_ACTIVATOR_FILE);
// If the activator file doesn't exist, then
// assume there isn't one.
- if (!activatorFile.exists())
+ if (!DefaultBundleCache.getSecureAction().fileExists(activatorFile))
+ {
return null;
+ }
// Deserialize the activator object.
InputStream is = null;
ObjectInputStreamX ois = null;
try
{
- is = new FileInputStream(activatorFile);
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(activatorFile);
ois = new ObjectInputStreamX(is, module);
Object o = ois.readObject();
return (BundleActivator) o;
@@ -568,7 +482,7 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "DefaultBundleArchive: Trying to deserialize - " + ex);
+ getClass().getName() + ": Trying to deserialize - " + ex);
}
finally
{
@@ -579,45 +493,27 @@
return null;
}
- public void setActivator(Object obj)
- throws Exception
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.SET_ACTIVATOR_ACTION, this, obj));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- setActivatorUnchecked(obj);
- }
- }
-
- private void setActivatorUnchecked(Object obj)
- throws Exception
+ /**
+ * <p>
+ * Serializes the activator for this archive.
+ * </p>
+ * @param obj the activator to serialize.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized void setActivator(Object obj) throws Exception
{
if (!(obj instanceof Serializable))
{
return;
}
- // Get bundle activator file.
- File activatorFile = new File(m_dir, BUNDLE_ACTIVATOR_FILE);
-
// Serialize the activator object.
OutputStream os = null;
ObjectOutputStream oos = null;
try
{
- os = new FileOutputStream(activatorFile);
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_ACTIVATOR_FILE));
oos = new ObjectOutputStream(os);
oos.writeObject(obj);
}
@@ -625,7 +521,7 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to serialize activator - " + ex);
+ getClass().getName() + ": Unable to serialize activator - " + ex);
throw ex;
}
finally
@@ -635,383 +531,398 @@
}
}
- public int getRevisionCount()
+ /**
+ * <p>
+ * Returns the number of revisions available for this archive.
+ * </p>
+ * @return tthe number of revisions available for this archive.
+ **/
+ public synchronized int getRevisionCount()
+ {
+ return (m_revisions == null) ? 0 : m_revisions.length;
+ }
+
+ /**
+ * <p>
+ * Returns the revision object for the specified revision.
+ * </p>
+ * @return the revision object for the specified revision.
+ **/
+ public synchronized BundleRevision getRevision(int i)
+ {
+ if ((i >= 0) && (i < getRevisionCount()))
+ {
+ return m_revisions[i];
+ }
+ return null;
+ }
+
+ /**
+ * <p>
+ * This method adds a revision to the archive. The revision is created
+ * based on the specified location and/or input stream.
+ * </p>
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized void revise(String location, InputStream is)
throws Exception
{
- if (System.getSecurityManager() != null)
+ // If we have an input stream, then we have to use it
+ // no matter what the update location is, so just ignore
+ // the update location and set the location to be input
+ // stream.
+ if (is != null)
{
- try
- {
- return ((Integer) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_REVISION_COUNT_ACTION, this))).intValue();
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
+ location = "inputstream:";
+ }
+ BundleRevision revision = createRevisionFromLocation(location, is);
+ if (revision == null)
+ {
+ throw new Exception("Unable to revise archive.");
+ }
+
+ // Set the current revision location to match.
+ setCurrentLocation(location);
+
+ // Add new revision to revision array.
+ if (m_revisions == null)
+ {
+ m_revisions = new BundleRevision[] { revision };
}
else
{
- return getRevisionCountUnchecked();
+ BundleRevision[] tmp = new BundleRevision[m_revisions.length + 1];
+ System.arraycopy(m_revisions, 0, tmp, 0, m_revisions.length);
+ tmp[m_revisions.length] = revision;
+ m_revisions = tmp;
}
}
- public int getRevisionCountUnchecked()
+ /**
+ * <p>
+ * This method removes all old revisions associated with the archive
+ * and keeps only the current revision.
+ * </p>
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized void purge() throws Exception
{
- // We should always have at least one revision
- // directory, so try to count them if the value
- // has not been initialized yet.
- if (m_revisionCount <= 0)
+ // Get the current refresh count.
+ long refreshCount = getRefreshCount();
+ // Get the current revision count.
+ int count = getRevisionCount();
+
+ // Dispose and delete all but the current revision.
+ File revisionDir = null;
+ for (int i = 0; i < count - 1; i++)
{
- m_revisionCount = 0;
- File[] children = m_dir.listFiles();
- for (int i = 0; (children != null) && (i < children.length); i++)
+ m_revisions[i].dispose();
+ revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY + refreshCount + "." + i);
+ if (DefaultBundleCache.getSecureAction().fileExists(revisionDir))
{
- if (children[i].getName().startsWith(REVISION_DIRECTORY))
- {
- m_revisionCount++;
- }
+ DefaultBundleCache.deleteDirectoryTree(revisionDir);
}
}
- return m_revisionCount;
+
+ // We still need to dispose the current revision, but we
+ // don't want to delete it, because we want to rename it
+ // to the new refresh level.
+ m_revisions[count - 1].dispose();
+
+ // Increment the refresh count.
+ setRefreshCount(refreshCount + 1);
+
+ // Rename the current revision directory to be the zero revision
+ // of the new refresh level.
+ File currentDir = new File(m_archiveRootDir, REVISION_DIRECTORY + (refreshCount + 1) + ".0");
+ revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY + refreshCount + "." + (count - 1));
+ DefaultBundleCache.getSecureAction().renameFile(revisionDir, currentDir);
+
+ // Null the revision array since they are all invalid now.
+ m_revisions = null;
+ // Finally, recreate the revision for the current location.
+ BundleRevision revision = createRevisionFromLocation(getCurrentLocation(), null);
+ // Create new revision array.
+ m_revisions = new BundleRevision[] { revision };
}
- public Map getManifestHeader(int revision)
- throws Exception
+ /**
+ * <p>
+ * This method disposes removes the bundle archive directory.
+ * </p>
+ * @throws Exception if any error occurs.
+ **/
+ /* package */ void dispose() throws Exception
{
- // If the request is for the current revision header,
- // then return the cached copy if it is present.
- if ((revision == (getRevisionCount() - 1)) && (m_currentHeader != null))
+ if (!DefaultBundleCache.deleteDirectoryTree(m_archiveRootDir))
{
- return m_currentHeader;
+ m_logger.log(
+ Logger.LOG_ERROR,
+ getClass().getName()
+ + ": Unable to delete archive directory - "
+ + m_archiveRootDir);
}
+ }
- // Get the revision directory.
- File revisionDir = new File(
- m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
-
- // Get the embedded resource.
- JarFileX jarFile = null;
+ /**
+ * <p>
+ * Initializes the bundle archive object by creating the archive
+ * root directory and saving the initial state.
+ * </p>
+ * @throws Exception if any error occurs.
+ **/
+ private void initialize() throws Exception
+ {
+ OutputStream os = null;
+ BufferedWriter bw = null;
try
{
- // Open bundle JAR file.
- jarFile = openBundleJar(revisionDir);
- // Error if no jar file.
- if (jarFile == null)
+ // If the archive directory exists, then we don't
+ // need to initialize since it has already been done.
+ if (DefaultBundleCache.getSecureAction().fileExists(m_archiveRootDir))
{
- throw new IOException("No JAR file found.");
+ return;
}
- // Get manifest.
- Manifest mf = jarFile.getManifest();
- // Create a case insensitive map of manifest attributes.
- Map map = new StringMap(mf.getMainAttributes(), false);
- // If the request is for the current revision's header,
- // then cache it.
- if (revision == (getRevisionCount() - 1))
- {
- m_currentHeader = map;
- }
- return map;
+ // Create archive directory, if it does not exist.
+ if (!DefaultBundleCache.getSecureAction().mkdir(m_archiveRootDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ getClass().getName() + ": Unable to create archive directory.");
+ throw new IOException("Unable to create archive directory.");
+ }
+
+ // Save id.
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_ID_FILE));
+ bw = new BufferedWriter(new OutputStreamWriter(os));
+ bw.write(Long.toString(m_id), 0, Long.toString(m_id).length());
+ bw.close();
+ os.close();
+
+ // Save location string.
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, BUNDLE_LOCATION_FILE));
+ bw = new BufferedWriter(new OutputStreamWriter(os));
+ bw.write(m_originalLocation, 0, m_originalLocation.length());
}
finally
{
- if (jarFile != null) jarFile.close();
+ if (bw != null) bw.close();
+ if (os != null) os.close();
}
}
- private JarFileX openBundleJarUnchecked(File revisionDir)
- throws Exception
+ /**
+ * <p>
+ * Returns the current location associated with the bundle archive,
+ * which is the last location from which the bundle was updated. It is
+ * necessary to keep track of this so it is possible to determine what
+ * kind of revision needs to be created when recreating revisions when
+ * the framework restarts.
+ * @return the last update location.
+ * @throws Exception if any error occurs.
+ **/
+ private String getCurrentLocation() throws Exception
{
- // Get bundle jar file.
- File bundleJar = new File(revisionDir, BUNDLE_JAR_FILE);
- // Get bundle jar file.
- return new JarFileX(bundleJar);
- }
-
- private JarFileX openBundleJar(File revisionDir) throws Exception
- {
- // Create JarFile object using privileged block.
- if (System.getSecurityManager() != null)
+ if (m_currentLocation != null)
{
- try
- {
- return (JarFileX) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.OPEN_BUNDLE_JAR_ACTION, this, revisionDir));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
+ return m_currentLocation;
}
- return openBundleJarUnchecked(revisionDir);
- }
-
- private IContent getContentUnchecked(int revision)
- throws Exception
- {
- // Get the revision directory.
- File revisionDir = new File(
- m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
- // Return a content object for the bundle itself.
- return new JarContent(new File(revisionDir, BUNDLE_JAR_FILE));
- }
-
- public IContent getContent(int revision)
- throws Exception
- {
- IContent content = null;
-
- if (System.getSecurityManager() != null)
- {
- try
- {
- content = (IContent) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_CONTENT_ACTION, this, revision));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- content = getContentUnchecked(revision);
- }
-
- return content;
- }
-
- private IContent[] getContentPathUnchecked(int revision)
- throws Exception
- {
- // Creating the content path entails examining the bundle's
- // class path to determine whether the bundle JAR file itself
- // is on the bundle's class path and then creating content
- // objects for everything on the class path.
-
- // Get the revision directory.
- File revisionDir = new File(
- m_dir, REVISION_DIRECTORY + getRefreshCount() + "." + revision);
- File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
-
- // Get the bundle's manifest header.
- Map map = getManifestHeader(revision);
-
- // Find class path meta-data.
- String classPath = (map == null)
- ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-
- // Parse the class path into strings.
- String[] classPathStrings = Util.parseDelimitedString(
- classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-
- if (classPathStrings == null)
- {
- classPathStrings = new String[0];
- }
-
- // Create the bundles class path.
- JarFileX bundleJar = null;
+ // Read current location.
+ InputStream is = null;
+ BufferedReader br = null;
try
{
- bundleJar = openBundleJar(revisionDir);
- IContent self = new JarContent(new File(revisionDir, BUNDLE_JAR_FILE));
- IContent[] contentPath = new IContent[classPathStrings.length];
- for (int i = 0; i < classPathStrings.length; i++)
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(new File(m_archiveRootDir, CURRENT_LOCATION_FILE));
+ br = new BufferedReader(new InputStreamReader(is));
+ m_currentLocation = br.readLine();
+ return m_currentLocation;
+ }
+ catch (FileNotFoundException ex)
+ {
+ return getLocation();
+ }
+ finally
+ {
+ if (br != null) br.close();
+ if (is != null) is.close();
+ }
+ }
+
+ /**
+ * <p>
+ * Set the current location associated with the bundle archive,
+ * which is the last location from which the bundle was updated. It is
+ * necessary to keep track of this so it is possible to determine what
+ * kind of revision needs to be created when recreating revisions when
+ * the framework restarts.
+ * @throws Exception if any error occurs.
+ **/
+ private void setCurrentLocation(String location) throws Exception
+ {
+ // Save current location.
+ OutputStream os = null;
+ BufferedWriter bw = null;
+ try
+ {
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(new File(m_archiveRootDir, CURRENT_LOCATION_FILE));
+ bw = new BufferedWriter(new OutputStreamWriter(os));
+ bw.write(location, 0, location.length());
+ m_currentLocation = location;
+ }
+ finally
+ {
+ if (bw != null) bw.close();
+ if (os != null) os.close();
+ }
+ }
+
+ /**
+ * <p>
+ * Creates a revision based on the location string and/or input stream.
+ * </p>
+ * @return the location string associated with this archive.
+ **/
+ private BundleRevision createRevisionFromLocation(String location, InputStream is)
+ throws Exception
+ {
+ // The revision directory is name using the refresh count and
+ // the revision count. The revision count is obvious, but the
+ // refresh count is less obvious. This is necessary due to how
+ // native libraries are handled in Java; needless to say, every
+ // time a bundle is refreshed we must change the name of its
+ // native libraries so that we can reload them. Thus, we use the
+ // refresh counter as a way to change the name of the revision
+ // directory to give native libraries new absolute names.
+ File revisionRootDir = new File(m_archiveRootDir,
+ REVISION_DIRECTORY + getRefreshCount() + "." + getRevisionCount());
+
+ try
+ {
+ // Check if the location string represents a reference URL.
+ if ((location != null) && location.startsWith(REFERENCE_PROTOCOL))
{
- if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ // Reference URLs only support the file protocol.
+ location = location.substring(REFERENCE_PROTOCOL.length());
+ if (!location.startsWith(FILE_PROTOCOL))
{
- contentPath[i] = self;
+ throw new IOException("Reference URLs can only be files: " + location);
+ }
+
+ // Make sure the referenced file exists.
+ File file = new File(location.substring(FILE_PROTOCOL.length()));
+ if (!DefaultBundleCache.getSecureAction().fileExists(file))
+ {
+ throw new IOException("Referenced file does not exist: " + file);
+ }
+
+ // If the referenced file is a directory, then create a directory
+ // revision; otherwise, create a JAR revision with the reference
+ // flag set to true.
+ if (DefaultBundleCache.getSecureAction().isFileDirectory(file))
+ {
+ return new DirectoryRevision(m_logger, revisionRootDir, location);
}
else
{
- // Determine if the class path entry is a file or directory
- // in the bundle JAR file.
- ZipEntry entry = bundleJar.getEntry(classPathStrings[i]);
- if ((entry != null) && entry.isDirectory())
- {
- contentPath[i] = new ContentDirectoryContent(self, classPathStrings[i]);
- }
- else
- {
- contentPath[i] = new JarContent(new File(embedDir, classPathStrings[i]));
- }
+ return new JarRevision(m_logger, revisionRootDir, location, true);
}
}
-
- // If there is nothing on the class path, then include
- // "." by default, as per the spec.
- if (contentPath.length == 0)
+ else if (location.startsWith(INPUTSTREAM_PROTOCOL))
{
- contentPath = new IContent[] { self };
+ // Assume all input streams point to JAR files.
+ return new JarRevision(m_logger, revisionRootDir, location, false, is);
}
-
- return contentPath;
- }
- finally
- {
- if (bundleJar != null) bundleJar.close();
- }
- }
-
- public IContent[] getContentPath(int revision)
- throws Exception
- {
- IContent[] contents = null;
-
- if (System.getSecurityManager() != null)
- {
- try
+ else
{
- contents = (IContent[]) AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.GET_CONTENT_PATH_ACTION, this, revision));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
+ // Anything else is assumed to be a URL to a JAR file.
+ return new JarRevision(m_logger, revisionRootDir, location, false);
}
}
- else
+ catch (Exception ex)
{
- contents = getContentPathUnchecked(revision);
- }
-
- return contents;
- }
-
-// TODO: This will need to consider security.
- public String findLibrary(int revision, String libName)
- throws Exception
- {
- return findLibraryUnchecked(revision, libName);
- }
-
- private String findLibraryUnchecked(int revision, String libName)
- throws Exception
- {
- // Get the revision directory.
- File revisionDir = new File(
- m_dir.getAbsoluteFile(),
- REVISION_DIRECTORY + getRefreshCount() + "." + revision);
-
- // Get bundle lib directory.
- File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
- // Get lib file.
- File libFile = new File(libDir, File.separatorChar + libName);
- // Make sure that the library's parent directory exists;
- // it may be in a sub-directory.
- libDir = libFile.getParentFile();
- if (!libDir.exists())
- {
- if (!libDir.mkdirs())
+ if (DefaultBundleCache.getSecureAction().fileExists(revisionRootDir))
{
- throw new IOException("Unable to create library directory.");
- }
- }
- // Extract the library from the JAR file if it does not
- // already exist.
- if (!libFile.exists())
- {
- JarFileX jarFile = null;
- InputStream is = null;
-
- try
- {
- jarFile = openBundleJarUnchecked(revisionDir);
- ZipEntry ze = jarFile.getEntry(libName);
- if (ze == null)
+ if (!DefaultBundleCache.deleteDirectoryTree(revisionRootDir))
{
- throw new IOException("No JAR entry: " + libName);
+ m_logger.log(
+ Logger.LOG_ERROR,
+ getClass().getName()
+ + ": Unable to delete revision directory - "
+ + revisionRootDir);
}
- is = new BufferedInputStream(
- jarFile.getInputStream(ze), DefaultBundleCache.BUFSIZE);
- if (is == null)
- {
- throw new IOException("No input stream: " + libName);
- }
-
- // Create the file.
- copy(is, libFile);
-
}
- finally
- {
- if (jarFile != null) jarFile.close();
- if (is != null) is.close();
- }
+ throw ex;
}
-
- return libFile.toString();
}
/**
* This utility method is used to retrieve the current refresh
* counter value for the bundle. This value is used when generating
- * the bundle JAR directory name where native libraries are extracted.
+ * the bundle revision directory name where native libraries are extracted.
* This is necessary because Sun's JVM requires a one-to-one mapping
* between native libraries and class loaders where the native library
* is uniquely identified by its absolute path in the file system. This
* constraint creates a problem when a bundle is refreshed, because it
* gets a new class loader. Using the refresh counter to generate the name
- * of the bundle JAR directory resolves this problem because each time
+ * of the bundle revision directory resolves this problem because each time
* bundle is refresh, the native library will have a unique name.
* As a result of the unique name, the JVM will then reload the
* native library without a problem.
**/
- private long getRefreshCount()
- throws Exception
+ private long getRefreshCount() throws Exception
{
- // If we have already read the update counter file,
+ // If we have already read the refresh counter file,
// then just return the result.
if (m_refreshCount >= 0)
{
return m_refreshCount;
}
- // Get update counter file.
- File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
+ // Get refresh counter file.
+ File counterFile = new File(m_archiveRootDir, REFRESH_COUNTER_FILE);
- // If the update counter file doesn't exist, then
+ // If the refresh counter file doesn't exist, then
// assume the counter is at zero.
- if (!counterFile.exists())
+ if (!DefaultBundleCache.getSecureAction().fileExists(counterFile))
{
return 0;
}
- // Read the bundle update counter.
- FileReader fr = null;
+ // Read the bundle refresh counter.
+ InputStream is = null;
BufferedReader br = null;
try
{
- fr = new FileReader(counterFile);
- br = new BufferedReader(fr);
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(counterFile);
+ br = new BufferedReader(new InputStreamReader(is));
long counter = Long.parseLong(br.readLine());
return counter;
}
finally
{
if (br != null) br.close();
- if (fr != null) fr.close();
+ if (is != null) is.close();
}
}
/**
* This utility method is used to retrieve the current refresh
* counter value for the bundle. This value is used when generating
- * the bundle JAR directory name where native libraries are extracted.
+ * the bundle revision directory name where native libraries are extracted.
* This is necessary because Sun's JVM requires a one-to-one mapping
* between native libraries and class loaders where the native library
* is uniquely identified by its absolute path in the file system. This
* constraint creates a problem when a bundle is refreshed, because it
* gets a new class loader. Using the refresh counter to generate the name
- * of the bundle JAR directory resolves this problem because each time
+ * of the bundle revision directory resolves this problem because each time
* bundle is refresh, the native library will have a unique name.
* As a result of the unique name, the JVM will then reload the
* native library without a problem.
@@ -1019,16 +930,17 @@
private void setRefreshCount(long counter)
throws Exception
{
- // Get update counter file.
- File counterFile = new File(m_dir, REFRESH_COUNTER_FILE);
+ // Get refresh counter file.
+ File counterFile = new File(m_archiveRootDir, REFRESH_COUNTER_FILE);
- // Write the update counter.
- FileWriter fw = null;
+ // Write the refresh counter.
+ OutputStream os = null;
BufferedWriter bw = null;
try
{
- fw = new FileWriter(counterFile);
- bw = new BufferedWriter(fw);
+ os = DefaultBundleCache.getSecureAction()
+ .getFileOutputStream(counterFile);
+ bw = new BufferedWriter(new OutputStreamWriter(os));
String s = Long.toString(counter);
bw.write(s, 0, s.length());
m_refreshCount = counter;
@@ -1037,477 +949,13 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "DefaultBundleArchive: Unable to write counter: " + ex);
+ getClass().getName() + ": Unable to write refresh counter: " + ex);
throw ex;
}
finally
{
if (bw != null) bw.close();
- if (fw != null) fw.close();
- }
- }
-
- //
- // File-oriented utility methods.
- //
-
- protected static boolean deleteDirectoryTree(File target)
- {
- if (!target.exists())
- {
- return true;
- }
-
- if (target.isDirectory())
- {
- File[] files = target.listFiles();
- for (int i = 0; i < files.length; i++)
- {
- deleteDirectoryTree(files[i]);
- }
- }
-
- return target.delete();
- }
-
- /**
- * This method copies an input stream to the specified file.
- * <p>
- * Security: This method must be called from within a <tt>doPrivileged()</tt>
- * block since it accesses the disk.
- * @param is the input stream to copy.
- * @param outputFile the file to which the input stream should be copied.
- **/
- private void copy(InputStream is, File outputFile)
- throws IOException
- {
- OutputStream os = null;
-
- try
- {
- os = new BufferedOutputStream(
- new FileOutputStream(outputFile), DefaultBundleCache.BUFSIZE);
- byte[] b = new byte[DefaultBundleCache.BUFSIZE];
- int len = 0;
- while ((len = is.read(b)) != -1)
- {
- os.write(b, 0, len);
- }
- }
- finally
- {
- if (is != null) is.close();
if (os != null) os.close();
}
}
-
- /**
- * This method pre-processes a bundle JAR file making it ready
- * for use. This entails extracting all embedded JAR files and
- * all native libraries.
- * @throws java.lang.Exception if any error occurs while processing JAR file.
- **/
- private void preprocessBundleJar(int revision, File revisionDir)
- throws Exception
- {
- //
- // Create special directories so that we can avoid checking
- // for their existence all the time.
- //
-
- File embedDir = new File(revisionDir, EMBEDDED_DIRECTORY);
- if (!embedDir.exists())
- {
- if (!embedDir.mkdir())
- {
- throw new IOException("Could not create embedded JAR directory.");
- }
- }
-
- File libDir = new File(revisionDir, LIBRARY_DIRECTORY);
- if (!libDir.exists())
- {
- if (!libDir.mkdir())
- {
- throw new IOException("Unable to create native library directory.");
- }
- }
-
- //
- // This block extracts all embedded JAR files.
- //
-
- try
- {
- // Get the bundle's manifest header.
- Map map = getManifestHeader(revision);
-
- // Find class path meta-data.
- String classPath = (map == null)
- ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-
- // Parse the class path into strings.
- String[] classPathStrings = Util.parseDelimitedString(
- classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-
- if (classPathStrings == null)
- {
- classPathStrings = new String[0];
- }
-
- for (int i = 0; i < classPathStrings.length; i++)
- {
- if (!classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
- {
- extractEmbeddedJar(revisionDir, classPathStrings[i]);
- }
- }
-
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
-
- /**
- * This method extracts an embedded JAR file from the bundle's
- * JAR file.
- * <p>
- * Security: This method must be called from within a <tt>doPrivileged()</tt>
- * block since it accesses the disk.
- * @param id the identifier of the bundle that owns the embedded JAR file.
- * @param jarPath the path to the embedded JAR file inside the bundle JAR file.
- **/
- private void extractEmbeddedJar(File revisionDir, String jarPath)
- throws Exception
- {
- // Remove leading slash if present.
- jarPath = (jarPath.charAt(0) == '/') ? jarPath.substring(1) : jarPath;
-
- // If JAR is already extracted, then don't re-extract it...
- File jarFile = new File(
- revisionDir, EMBEDDED_DIRECTORY + File.separatorChar + jarPath);
-
- if (!jarFile.exists())
- {
- JarFileX bundleJar = null;
- InputStream is = null;
- try
- {
- // Make sure class path entry is a JAR file.
- bundleJar = openBundleJarUnchecked(revisionDir);
- ZipEntry ze = bundleJar.getEntry(jarPath);
- if (ze == null)
- {
- throw new IOException("No JAR entry: " + jarPath);
- }
- // If the zip entry is a directory, then ignore it since
- // we don't need to extact it; otherwise, it points to an
- // embedded JAR file, so extract it.
- else if (!ze.isDirectory())
- {
- // Make sure that the embedded JAR's parent directory exists;
- // it may be in a sub-directory.
- File jarDir = jarFile.getParentFile();
- if (!jarDir.exists())
- {
- if (!jarDir.mkdirs())
- {
- throw new IOException("Unable to create embedded JAR directory.");
- }
- }
-
- // Extract embedded JAR into its directory.
- is = new BufferedInputStream(bundleJar.getInputStream(ze), DefaultBundleCache.BUFSIZE);
- if (is == null)
- {
- throw new IOException("No input stream: " + jarPath);
- }
- // Copy the file.
- copy(is, jarFile);
- }
- }
- finally
- {
- if (bundleJar != null) bundleJar.close();
- if (is != null) is.close();
- }
- }
- }
-
- // INCREASES THE REVISION COUNT.
- protected void update(InputStream is) throws Exception
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.UPDATE_ACTION, this, is));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- updateUnchecked(is);
- }
- }
-
- // INCREASES THE REVISION COUNT.
- private void updateUnchecked(InputStream is) throws Exception
- {
- File revisionDir = null;
-
- try
- {
- // Create the new revision directory.
- int revision = getRevisionCountUnchecked();
- revisionDir = new File(
- m_dir, REVISION_DIRECTORY
- + getRefreshCount() + "." + revision);
- if (!revisionDir.mkdir())
- {
- throw new IOException("Unable to create revision directory.");
- }
-
- // Save the new revision bundle jar file.
- File file = new File(revisionDir, BUNDLE_JAR_FILE);
- copy(is, file);
-
- preprocessBundleJar(revision, revisionDir);
- }
- catch (Exception ex)
- {
- if ((revisionDir != null) && revisionDir.exists())
- {
- try
- {
- deleteDirectoryTree(revisionDir);
- }
- catch (Exception ex2)
- {
- // There is very little we can do here.
- m_logger.log(
- Logger.LOG_ERROR,
- "Unable to remove partial revision directory.", ex2);
- }
- }
- throw ex;
- }
-
- // If everything was successful, then update
- // the revision count.
- m_revisionCount++;
- // Clear the cached revision header, since it is
- // no longer the current revision.
- m_currentHeader = null;
- }
-
- // DECREASES THE REVISION COUNT.
- protected void purge() throws Exception
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.PURGE_ACTION, this));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- purgeUnchecked();
- }
- }
-
- // DECREASES THE REVISION COUNT.
- private void purgeUnchecked() throws Exception
- {
- // Get the current update count.
- long update = getRefreshCount();
- // Get the current revision count.
- int count = getRevisionCountUnchecked();
-
- File revisionDir = null;
- for (int i = 0; i < count - 1; i++)
- {
- revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + i);
- if (revisionDir.exists())
- {
- deleteDirectoryTree(revisionDir);
- }
- }
- // Increment the update count.
- setRefreshCount(update + 1);
-
- // Rename the current revision to be the current update.
- File currentDir = new File(m_dir, REVISION_DIRECTORY + (update + 1) + ".0");
- revisionDir = new File(m_dir, REVISION_DIRECTORY + update + "." + (count - 1));
- revisionDir.renameTo(currentDir);
-
- // If everything is successful, then set the revision
- // count to one.
- m_revisionCount = 1;
- // Although the cached current header should stay the same
- // here, clear it for consistency.
- m_currentHeader = null;
- }
-
- protected void remove() throws Exception
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- AccessController.doPrivileged(
- new PrivilegedAction(
- PrivilegedAction.REMOVE_ACTION, this));
- }
- catch (PrivilegedActionException ex)
- {
- throw ((PrivilegedActionException) ex).getException();
- }
- }
- else
- {
- removeUnchecked();
- }
- }
-
- private void removeUnchecked() throws Exception
- {
- deleteDirectoryTree(m_dir);
- }
-
- //
- // Utility class for performing privileged actions.
- //
-
- private static class PrivilegedAction implements PrivilegedExceptionAction
- {
- private static final int INITIALIZE_ACTION = 0;
- private static final int UPDATE_ACTION = 1;
- private static final int PURGE_ACTION = 2;
- private static final int REMOVE_ACTION = 3;
- private static final int GET_REVISION_COUNT_ACTION = 4;
- private static final int GET_LOCATION_ACTION = 5;
- private static final int GET_PERSISTENT_STATE_ACTION = 6;
- private static final int SET_PERSISTENT_STATE_ACTION = 7;
- private static final int GET_START_LEVEL_ACTION = 8;
- private static final int SET_START_LEVEL_ACTION = 9;
- private static final int OPEN_BUNDLE_JAR_ACTION = 10;
- private static final int CREATE_DATA_DIR_ACTION = 11;
- private static final int GET_CONTENT_ACTION = 12;
- private static final int GET_CONTENT_PATH_ACTION = 13;
- private static final int GET_ACTIVATOR_ACTION = 14;
- private static final int SET_ACTIVATOR_ACTION = 15;
-
- private int m_action = 0;
- private DefaultBundleArchive m_archive = null;
- private InputStream m_isArg = null;
- private int m_intArg = 0;
- private File m_fileArg = null;
- private IModule m_moduleArg = null;
- private Object m_objArg = null;
-
- public PrivilegedAction(int action, DefaultBundleArchive archive)
- {
- m_action = action;
- m_archive = archive;
- }
-
- public PrivilegedAction(int action, DefaultBundleArchive archive, InputStream isArg)
- {
- m_action = action;
- m_archive = archive;
- m_isArg = isArg;
- }
-
- public PrivilegedAction(int action, DefaultBundleArchive archive, int intArg)
- {
- m_action = action;
- m_archive = archive;
- m_intArg = intArg;
- }
-
- public PrivilegedAction(int action, DefaultBundleArchive archive, File fileArg)
- {
- m_action = action;
- m_archive = archive;
- m_fileArg = fileArg;
- }
-
- public PrivilegedAction(int action, DefaultBundleArchive archive, IModule moduleArg)
- {
- m_action = action;
- m_archive = archive;
- m_moduleArg = moduleArg;
- }
-
- public PrivilegedAction(int action, DefaultBundleArchive archive, Object objArg)
- {
- m_action = action;
- m_archive = archive;
- m_objArg = objArg;
- }
-
- public Object run() throws Exception
- {
- switch (m_action)
- {
- case INITIALIZE_ACTION:
- m_archive.initializeUnchecked(m_isArg);
- return null;
- case UPDATE_ACTION:
- m_archive.updateUnchecked(m_isArg);
- return null;
- case PURGE_ACTION:
- m_archive.purgeUnchecked();
- return null;
- case REMOVE_ACTION:
- m_archive.removeUnchecked();
- return null;
- case GET_REVISION_COUNT_ACTION:
- return new Integer(m_archive.getRevisionCountUnchecked());
- case GET_LOCATION_ACTION:
- return m_archive.getLocationUnchecked();
- case GET_PERSISTENT_STATE_ACTION:
- return new Integer(m_archive.getPersistentStateUnchecked());
- case SET_PERSISTENT_STATE_ACTION:
- m_archive.setPersistentStateUnchecked(m_intArg);
- return null;
- case GET_START_LEVEL_ACTION:
- return new Integer(m_archive.getStartLevelUnchecked());
- case SET_START_LEVEL_ACTION:
- m_archive.setStartLevelUnchecked(m_intArg);
- return null;
- case OPEN_BUNDLE_JAR_ACTION:
- return m_archive.openBundleJarUnchecked(m_fileArg);
- case CREATE_DATA_DIR_ACTION:
- m_archive.createDataDirectoryUnchecked(m_fileArg);
- return null;
- case GET_CONTENT_ACTION:
- return m_archive.getContentUnchecked(m_intArg);
- case GET_CONTENT_PATH_ACTION:
- return m_archive.getContentPathUnchecked(m_intArg);
- case GET_ACTIVATOR_ACTION:
- return m_archive.getActivatorUnchecked(m_moduleArg);
- case SET_ACTIVATOR_ACTION:
- m_archive.setActivatorUnchecked(m_objArg);
- return null;
- }
-
- throw new IllegalArgumentException("Invalid action specified.");
- }
- }
}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleCache.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleCache.java
index eb0499d..b45cd54 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleCache.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DefaultBundleCache.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2005 The Apache Software Foundation
+ * Copyright 2006 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,16 +17,20 @@
package org.apache.felix.framework.cache;
import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.PropertyResolver;
+import org.apache.felix.framework.util.SecureAction;
/**
* <p>
- * This class, combined with <tt>DefaultBundleArchive</tt>, implements the
- * default file system-based bundle cache for Felix. It is possible to
- * configure the default behavior of this class by passing properties into
- * Felix constructor. The configuration properties for this class are:
+ * This class, combined with <tt>BundleArchive</tt>, and concrete
+ * <tt>BundleRevision</tt> subclasses, implement the Felix bundle cache.
+ * It is possible to configure the default behavior of this class by
+ * passing properties into Felix' constructor. The configuration properties
+ * for this class are:
* </p>
* <ul>
* <li><tt>felix.cache.bufsize</tt> - Sets the buffer size to be used by
@@ -65,7 +69,7 @@
* </p>
* @see org.apache.felix.framework.util.DefaultBundleArchive
**/
-public class DefaultBundleCache implements BundleCache
+public class DefaultBundleCache
{
public static final String CACHE_BUFSIZE_PROP = "felix.cache.bufsize";
public static final String CACHE_DIR_PROP = "felix.cache.dir";
@@ -79,19 +83,169 @@
private PropertyResolver m_cfg = null;
private Logger m_logger = null;
private File m_profileDir = null;
- private BundleArchive[] m_archives = null;
+ private DefaultBundleArchive[] m_archives = null;
- public DefaultBundleCache()
+ private static SecureAction m_secureAction = new SecureAction();
+
+ public DefaultBundleCache(PropertyResolver cfg, Logger logger)
+ throws Exception
{
+ m_cfg = cfg;
+ m_logger = logger;
+ initialize();
}
- public void initialize(PropertyResolver cfg, Logger logger) throws Exception
+ /* package */ static SecureAction getSecureAction()
{
- // Save Properties reference.
- m_cfg = cfg;
- // Save LogService reference.
- m_logger = logger;
+ return m_secureAction;
+ }
+ public synchronized DefaultBundleArchive[] getArchives()
+ throws Exception
+ {
+ return m_archives;
+ }
+
+ public synchronized DefaultBundleArchive getArchive(long id)
+ throws Exception
+ {
+ for (int i = 0; i < m_archives.length; i++)
+ {
+ if (m_archives[i].getId() == id)
+ {
+ return m_archives[i];
+ }
+ }
+ return null;
+ }
+
+ public synchronized int getArchiveIndex(DefaultBundleArchive ba)
+ {
+ for (int i = 0; i < m_archives.length; i++)
+ {
+ if (m_archives[i] == ba)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized DefaultBundleArchive create(long id, String location)
+ throws Exception
+ {
+ // Construct archive root directory.
+ File archiveRootDir =
+ new File(m_profileDir, BUNDLE_DIR_PREFIX + Long.toString(id));
+
+ try
+ {
+ // Create the archive and add it to the list of archives.
+ DefaultBundleArchive ba = new DefaultBundleArchive(m_logger, archiveRootDir, id, location);
+ DefaultBundleArchive[] tmp = new DefaultBundleArchive[m_archives.length + 1];
+ System.arraycopy(m_archives, 0, tmp, 0, m_archives.length);
+ tmp[m_archives.length] = ba;
+ m_archives = tmp;
+ return ba;
+ }
+ catch (Exception ex)
+ {
+ if (m_secureAction.fileExists(archiveRootDir))
+ {
+ if (!DefaultBundleCache.deleteDirectoryTree(archiveRootDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ getClass().getName()
+ + ": Unable to delete the archive directory - "
+ + archiveRootDir);
+ }
+ }
+ throw ex;
+ }
+ }
+
+ public synchronized void remove(DefaultBundleArchive ba)
+ throws Exception
+ {
+ // Remove the archive.
+ ba.dispose();
+ // Remove the archive from the cache.
+ int idx = getArchiveIndex(ba);
+ if (idx >= 0)
+ {
+ DefaultBundleArchive[] tmp =
+ new DefaultBundleArchive[m_archives.length - 1];
+ System.arraycopy(m_archives, 0, tmp, 0, idx);
+ if (idx < tmp.length)
+ {
+ System.arraycopy(m_archives, idx + 1, tmp, idx,
+ tmp.length - idx);
+ }
+ m_archives = tmp;
+ }
+ }
+
+ //
+ // Static file-related utility methods.
+ //
+
+ /**
+ * This method copies an input stream to the specified file.
+ * <p>
+ * Security: This method must be called from within a <tt>doPrivileged()</tt>
+ * block since it accesses the disk.
+ * @param is the input stream to copy.
+ * @param outputFile the file to which the input stream should be copied.
+ **/
+ protected static void copyStreamToFile(InputStream is, File outputFile)
+ throws IOException
+ {
+ OutputStream os = null;
+
+ try
+ {
+ os = getSecureAction().getFileOutputStream(outputFile);
+ os = new BufferedOutputStream(os, BUFSIZE);
+ byte[] b = new byte[BUFSIZE];
+ int len = 0;
+ while ((len = is.read(b)) != -1)
+ {
+ os.write(b, 0, len);
+ }
+ }
+ finally
+ {
+ if (is != null) is.close();
+ if (os != null) os.close();
+ }
+ }
+
+ protected static boolean deleteDirectoryTree(File target)
+ {
+ if (!getSecureAction().fileExists(target))
+ {
+ return true;
+ }
+
+ if (getSecureAction().isFileDirectory(target))
+ {
+ File[] files = getSecureAction().listDirectory(target);
+ for (int i = 0; i < files.length; i++)
+ {
+ deleteDirectoryTree(files[i]);
+ }
+ }
+
+ return getSecureAction().deleteFile(target);
+ }
+
+ //
+ // Private methods.
+ //
+
+ private void initialize() throws Exception
+ {
// Get buffer size value.
try
{
@@ -148,141 +302,44 @@
m_profileDir = new File(cacheDirStr, profileName);
}
- // Create profile directory.
- if (!m_profileDir.exists())
+ // Create profile directory, if it does not exist.
+ if (!getSecureAction().fileExists(m_profileDir))
{
- if (!m_profileDir.mkdirs())
+ if (!getSecureAction().mkdirs(m_profileDir))
{
m_logger.log(
Logger.LOG_ERROR,
- "Unable to create directory: " + m_profileDir);
+ getClass().getName() + ": Unable to create directory: "
+ + m_profileDir);
throw new RuntimeException("Unable to create profile directory.");
}
}
// Create the existing bundle archives in the profile directory,
// if any exist.
- File[] children = m_profileDir.listFiles();
- int count = 0;
- for (int i = 0; (children != null) && (i < children.length); i++)
- {
- // Count the legitimate bundle directories.
- if (children[i].getName().startsWith(BUNDLE_DIR_PREFIX))
- {
- count++;
- }
- }
- m_archives = new BundleArchive[count];
- count = 0;
+ List archiveList = new ArrayList();
+ File[] children = getSecureAction().listDirectory(m_profileDir);
for (int i = 0; (children != null) && (i < children.length); i++)
{
// Ignore directories that aren't bundle directories.
if (children[i].getName().startsWith(BUNDLE_DIR_PREFIX))
{
- String id = children[i].getName().substring(BUNDLE_DIR_PREFIX.length());
- m_archives[count] = new DefaultBundleArchive(
- m_logger, children[i], Long.parseLong(id));
- count++;
+ // Recreate the bundle archive.
+ try
+ {
+ archiveList.add(
+ new DefaultBundleArchive(m_logger, children[i]));
+ }
+ catch (Exception ex)
+ {
+ // Log and ignore.
+ m_logger.log(Logger.LOG_ERROR,
+ getClass().getName() + ": Error creating archive.", ex);
+ }
}
}
+
+ m_archives = (DefaultBundleArchive[])
+ archiveList.toArray(new DefaultBundleArchive[archiveList.size()]);
}
-
- public BundleArchive[] getArchives()
- throws Exception
- {
- return m_archives;
- }
-
- public BundleArchive getArchive(long id)
- throws Exception
- {
- for (int i = 0; i < m_archives.length; i++)
- {
- if (m_archives[i].getId() == id)
- {
- return m_archives[i];
- }
- }
- return null;
- }
-
- public int getArchiveIndex(BundleArchive ba)
- {
- for (int i = 0; i < m_archives.length; i++)
- {
- if (m_archives[i] == ba)
- {
- return i;
- }
- }
- return -1;
- }
-
- public BundleArchive create(long id, String location, InputStream is)
- throws Exception
- {
- // Define new bundle's directory.
- File bundleDir = new File(m_profileDir, "bundle" + id);
-
- try
- {
- // Buffer the input stream.
- is = new BufferedInputStream(is, DefaultBundleCache.BUFSIZE);
- // Create an archive instance for the new bundle.
- BundleArchive ba = new DefaultBundleArchive(
- m_logger, bundleDir, id, location, is);
- // Add the archive instance to the list of bundle archives.
- BundleArchive[] bas = new BundleArchive[m_archives.length + 1];
- System.arraycopy(m_archives, 0, bas, 0, m_archives.length);
- bas[m_archives.length] = ba;
- m_archives = bas;
- return ba;
- }
- finally
- {
- if (is != null) is.close();
- }
- }
-
- public void update(BundleArchive ba, InputStream is)
- throws Exception
- {
- try
- {
- // Buffer the input stream.
- is = new BufferedInputStream(is, DefaultBundleCache.BUFSIZE);
- // Do the update.
- ((DefaultBundleArchive) ba).update(is);
- }
- finally
- {
- if (is != null) is.close();
- }
- }
-
- public void purge(BundleArchive ba)
- throws Exception
- {
- ((DefaultBundleArchive) ba).purge();
- }
-
- public void remove(BundleArchive ba)
- throws Exception
- {
- // Remove the archive itself.
- ((DefaultBundleArchive) ba).remove();
- // Remove the archive from the cache.
- int idx = getArchiveIndex(ba);
- if (idx >= 0)
- {
- BundleArchive[] bas = new BundleArchive[m_archives.length - 1];
- System.arraycopy(m_archives, 0, bas, 0, idx);
- if (idx < bas.length)
- {
- System.arraycopy(m_archives, idx + 1, bas, idx,
- bas.length - idx);
- }
- m_archives = bas;
- }
- }
-}
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
new file mode 100644
index 0000000..d57317e
--- /dev/null
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.*;
+import java.util.Map;
+import java.util.jar.Manifest;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.*;
+import org.apache.felix.moduleloader.*;
+
+/**
+ * <p>
+ * This class implements a bundle archive revision for exploded bundle
+ * JAR files. It uses the specified location directory "in-place" to
+ * execute the bundle and does not copy the bundle content at all.
+ * </p>
+**/
+class DirectoryRevision extends BundleRevision
+{
+ private File m_refDir = null;
+ private Map m_header = null;
+
+ public DirectoryRevision(
+ Logger logger, File revisionRootDir, String location)
+ throws Exception
+ {
+ super(logger, revisionRootDir, location);
+ m_refDir = new File(location.substring(
+ location.indexOf(DefaultBundleArchive.FILE_PROTOCOL)
+ + DefaultBundleArchive.FILE_PROTOCOL.length()));
+ }
+
+ public synchronized Map getManifestHeader()
+ throws Exception
+ {
+ if (m_header != null)
+ {
+ return m_header;
+ }
+
+ // Read the header file from the reference directory.
+ InputStream is = null;
+
+ try
+ {
+ // Open manifest file.
+ is = DefaultBundleCache.getSecureAction()
+ .getFileInputStream(new File(m_refDir, "META-INF/MANIFEST.MF"));
+ // Error if no jar file.
+ if (is == null)
+ {
+ throw new IOException("No manifest file found.");
+ }
+
+ // Get manifest.
+ Manifest mf = new Manifest(is);
+ // Create a case insensitive map of manifest attributes.
+ m_header = new StringMap(mf.getMainAttributes(), false);
+ return m_header;
+ }
+ finally
+ {
+ if (is != null) is.close();
+ }
+ }
+
+ public IContent getContent() throws Exception
+ {
+ return new DirectoryContent(m_refDir);
+ }
+
+ public synchronized IContent[] getContentPath() throws Exception
+ {
+ // Creating the content path entails examining the bundle's
+ // class path to determine whether the bundle JAR file itself
+ // is on the bundle's class path and then creating content
+ // objects for everything on the class path.
+
+ // Get the bundle's manifest header.
+ Map map = getManifestHeader();
+
+ // Find class path meta-data.
+ String classPath = (map == null)
+ ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
+
+ // Parse the class path into strings.
+ String[] classPathStrings = Util.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ // Create the bundles class path.
+ IContent self = new DirectoryContent(m_refDir);
+ IContent[] contentPath = new IContent[classPathStrings.length];
+ for (int i = 0; i < classPathStrings.length; i++)
+ {
+ if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ contentPath[i] = self;
+ }
+ else
+ {
+ // Determine if the class path entry is a file or directory.
+ File file = new File(m_refDir, classPathStrings[i]);
+ if (DefaultBundleCache.getSecureAction().isFileDirectory(file))
+ {
+ contentPath[i] = new DirectoryContent(file);
+ }
+ else
+ {
+ contentPath[i] = new JarContent(file);
+ }
+ }
+ }
+
+ // If there is nothing on the class path, then include
+ // "." by default, as per the spec.
+ if (contentPath.length == 0)
+ {
+ contentPath = new IContent[] { self };
+ }
+
+ return contentPath;
+ }
+
+// TODO: This will need to consider security.
+ public String findLibrary(String libName) throws Exception
+ {
+ return new File(m_refDir, libName).toString();
+ }
+
+ public void dispose() throws Exception
+ {
+ // Nothing to dispose of, since we don't maintain any state outside
+ // of the revision directory, which will be automatically deleted
+ // by the parent bundle archive.
+ }
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
new file mode 100644
index 0000000..59cfe82
--- /dev/null
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.PrivilegedActionException;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.*;
+import org.apache.felix.moduleloader.*;
+
+/**
+ * <p>
+ * This class implements a bundle archive revision for a standard bundle
+ * JAR file. The specified location is the URL of the JAR file. By default,
+ * the associated JAR file is copied into the revision's directory on the
+ * file system, but it is possible to mark the JAR as 'by reference', which
+ * will result in the bundle JAR be used 'in place' and not being copied. In
+ * either case, some of the contents may be extracted into the revision
+ * directory, such as embedded JAR files and native libraries.
+ * </p>
+**/
+class JarRevision extends BundleRevision
+{
+ private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
+ private static final transient String EMBEDDED_DIRECTORY = "embedded";
+ private static final transient String LIBRARY_DIRECTORY = "lib";
+
+ private File m_bundleFile = null;
+ private Map m_header = null;
+
+ public JarRevision(
+ Logger logger, File revisionRootDir, String location, boolean byReference)
+ throws Exception
+ {
+ this(logger, revisionRootDir, location, byReference, null);
+ }
+
+ public JarRevision(
+ Logger logger, File revisionRootDir, String location,
+ boolean byReference, InputStream is)
+ throws Exception
+ {
+ super(logger, revisionRootDir, location);
+
+ if (byReference)
+ {
+ m_bundleFile = new File(location.substring(
+ location.indexOf(DefaultBundleArchive.FILE_PROTOCOL)
+ + DefaultBundleArchive.FILE_PROTOCOL.length()));
+ }
+ else
+ {
+ m_bundleFile = new File(getRevisionRootDir(), BUNDLE_JAR_FILE);
+ }
+
+ // Save and process the bundle JAR.
+ initialize(byReference, is);
+ }
+
+ public synchronized Map getManifestHeader() throws Exception
+ {
+ if (m_header != null)
+ {
+ return m_header;
+ }
+
+ // Get the embedded resource.
+ JarFile jarFile = null;
+
+ try
+ {
+ // Open bundle JAR file.
+ jarFile = DefaultBundleCache.getSecureAction().openJAR(m_bundleFile);
+ // Error if no jar file.
+ if (jarFile == null)
+ {
+ throw new IOException("No JAR file found.");
+ }
+ // Get manifest.
+ Manifest mf = jarFile.getManifest();
+ // Create a case insensitive map of manifest attributes.
+ m_header = new StringMap(mf.getMainAttributes(), false);
+ return m_header;
+
+ }
+ finally
+ {
+ if (jarFile != null) jarFile.close();
+ }
+ }
+
+ public IContent getContent() throws Exception
+ {
+ return new JarContent(m_bundleFile);
+ }
+
+ public synchronized IContent[] getContentPath() throws Exception
+ {
+ // Creating the content path entails examining the bundle's
+ // class path to determine whether the bundle JAR file itself
+ // is on the bundle's class path and then creating content
+ // objects for everything on the class path.
+
+ File embedDir = new File(getRevisionRootDir(), EMBEDDED_DIRECTORY);
+
+ // Get the bundle's manifest header.
+ Map map = getManifestHeader();
+
+ // Find class path meta-data.
+ String classPath = (map == null)
+ ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
+
+ // Parse the class path into strings.
+ String[] classPathStrings = Util.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ // Create the bundles class path.
+ JarFile bundleJar = null;
+ try
+ {
+ bundleJar = DefaultBundleCache.getSecureAction().openJAR(m_bundleFile);
+ IContent self = new JarContent(m_bundleFile);
+ IContent[] contentPath = new IContent[classPathStrings.length];
+ for (int i = 0; i < classPathStrings.length; i++)
+ {
+ if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ contentPath[i] = self;
+ }
+ else
+ {
+ // Determine if the class path entry is a file or directory
+ // in the bundle JAR file.
+ ZipEntry entry = bundleJar.getEntry(classPathStrings[i]);
+ if ((entry != null) && entry.isDirectory())
+ {
+ contentPath[i] = new ContentDirectoryContent(self, classPathStrings[i]);
+ }
+ else
+ {
+ contentPath[i] = new JarContent(new File(embedDir, classPathStrings[i]));
+ }
+ }
+ }
+
+ // If there is nothing on the class path, then include
+ // "." by default, as per the spec.
+ if (contentPath.length == 0)
+ {
+ contentPath = new IContent[] { self };
+ }
+
+ return contentPath;
+ }
+ finally
+ {
+ if (bundleJar != null) bundleJar.close();
+ }
+ }
+
+// TODO: This will need to consider security.
+ public synchronized String findLibrary(String libName) throws Exception
+ {
+ // Get bundle lib directory.
+ File libDir = new File(getRevisionRootDir(), LIBRARY_DIRECTORY);
+ // Get lib file.
+ File libFile = new File(libDir, File.separatorChar + libName);
+ // Make sure that the library's parent directory exists;
+ // it may be in a sub-directory.
+ libDir = libFile.getParentFile();
+ if (!DefaultBundleCache.getSecureAction().fileExists(libDir))
+ {
+ if (!DefaultBundleCache.getSecureAction().mkdirs(libDir))
+ {
+ throw new IOException("Unable to create library directory.");
+ }
+ }
+ // Extract the library from the JAR file if it does not
+ // already exist.
+ if (!DefaultBundleCache.getSecureAction().fileExists(libFile))
+ {
+ JarFile bundleJar = null;
+ InputStream is = null;
+
+ try
+ {
+ bundleJar = DefaultBundleCache.getSecureAction().openJAR(m_bundleFile);
+ ZipEntry ze = bundleJar.getEntry(libName);
+ if (ze == null)
+ {
+ throw new IOException("No JAR entry: " + libName);
+ }
+ is = new BufferedInputStream(
+ bundleJar.getInputStream(ze), DefaultBundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + libName);
+ }
+
+ // Create the file.
+ DefaultBundleCache.copyStreamToFile(is, libFile);
+ }
+ finally
+ {
+ if (bundleJar != null) bundleJar.close();
+ if (is != null) is.close();
+ }
+ }
+
+ return libFile.toString();
+ }
+
+ public void dispose() throws Exception
+ {
+ // Nothing to dispose of, since we don't maintain any state outside
+ // of the revision directory, which will be automatically deleted
+ // by the parent bundle archive.
+ }
+
+ //
+ // Private methods.
+ //
+
+ private void initialize(boolean byReference, InputStream is)
+ throws Exception
+ {
+ try
+ {
+ // If the revision directory exists, then we don't
+ // need to initialize since it has already been done.
+ if (DefaultBundleCache.getSecureAction().fileExists(getRevisionRootDir()))
+ {
+ return;
+ }
+
+ // Create revision directory, if it does not exist.
+ if (!DefaultBundleCache.getSecureAction().mkdir(getRevisionRootDir()))
+ {
+ getLogger().log(
+ Logger.LOG_ERROR,
+ getClass().getName() + ": Unable to create revision directory.");
+ throw new IOException("Unable to create archive directory.");
+ }
+
+ if (!byReference)
+ {
+ if (is == null)
+ {
+ // Do it the manual way to have a chance to
+ // set request properties such as proxy auth.
+ URL url = new URL(getLocation());
+ URLConnection conn = url.openConnection();
+
+ // Support for http proxy authentication.
+ String auth = DefaultBundleCache.getSecureAction()
+ .getSystemProperty("http.proxyAuth", null);
+ if ((auth != null) && (auth.length() > 0))
+ {
+ if ("http".equals(url.getProtocol()) ||
+ "https".equals(url.getProtocol()))
+ {
+ String base64 = Util.base64Encode(auth);
+ conn.setRequestProperty(
+ "Proxy-Authorization", "Basic " + base64);
+ }
+ }
+ is = conn.getInputStream();
+ }
+
+ // Save the bundle jar file.
+ DefaultBundleCache.copyStreamToFile(is, m_bundleFile);
+ }
+
+ // This will always be revision zero.
+ preprocessBundleJar();
+ }
+ finally
+ {
+ if (is != null) is.close();
+ }
+ }
+
+ /**
+ * This method pre-processes a bundle JAR file making it ready
+ * for use. This entails extracting all embedded JAR files and
+ * all native libraries.
+ * @throws java.lang.Exception if any error occurs while processing JAR file.
+ **/
+ private void preprocessBundleJar() throws Exception
+ {
+ //
+ // Create special directories so that we can avoid checking
+ // for their existence all the time.
+ //
+
+ File embedDir = new File(getRevisionRootDir(), EMBEDDED_DIRECTORY);
+ if (!DefaultBundleCache.getSecureAction().fileExists(embedDir))
+ {
+ if (!DefaultBundleCache.getSecureAction().mkdir(embedDir))
+ {
+ throw new IOException("Could not create embedded JAR directory.");
+ }
+ }
+
+ File libDir = new File(getRevisionRootDir(), LIBRARY_DIRECTORY);
+ if (!DefaultBundleCache.getSecureAction().fileExists(libDir))
+ {
+ if (!DefaultBundleCache.getSecureAction().mkdir(libDir))
+ {
+ throw new IOException("Unable to create native library directory.");
+ }
+ }
+
+ //
+ // This block extracts all embedded JAR files.
+ //
+
+ try
+ {
+ // Get the bundle's manifest header.
+ Map map = getManifestHeader();
+
+ // Find class path meta-data.
+ String classPath = (map == null)
+ ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
+
+ // Parse the class path into strings.
+ String[] classPathStrings = Util.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ for (int i = 0; i < classPathStrings.length; i++)
+ {
+ if (!classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ extractEmbeddedJar(classPathStrings[i]);
+ }
+ }
+
+ }
+ catch (PrivilegedActionException ex)
+ {
+ throw ((PrivilegedActionException) ex).getException();
+ }
+ }
+
+ /**
+ * This method extracts an embedded JAR file from the bundle's
+ * JAR file.
+ * <p>
+ * Security: This method must be called from within a <tt>doPrivileged()</tt>
+ * block since it accesses the disk.
+ * @param id the identifier of the bundle that owns the embedded JAR file.
+ * @param jarPath the path to the embedded JAR file inside the bundle JAR file.
+ **/
+ private void extractEmbeddedJar(String jarPath)
+ throws Exception
+ {
+ // Remove leading slash if present.
+ jarPath = (jarPath.length() > 0) && (jarPath.charAt(0) == '/')
+ ? jarPath.substring(1) : jarPath;
+
+ // If JAR is already extracted, then don't re-extract it...
+ File jarFile = new File(
+ getRevisionRootDir(), EMBEDDED_DIRECTORY + File.separatorChar + jarPath);
+
+ if (!DefaultBundleCache.getSecureAction().fileExists(jarFile))
+ {
+ JarFile bundleJar = null;
+ InputStream is = null;
+ try
+ {
+ // Make sure class path entry is a JAR file.
+ bundleJar = DefaultBundleCache.getSecureAction().openJAR(m_bundleFile);
+ ZipEntry ze = bundleJar.getEntry(jarPath);
+ if (ze == null)
+ {
+ throw new IOException("No JAR entry: " + jarPath);
+ }
+ // If the zip entry is a directory, then ignore it since
+ // we don't need to extact it; otherwise, it points to an
+ // embedded JAR file, so extract it.
+ else if (!ze.isDirectory())
+ {
+ // Make sure that the embedded JAR's parent directory exists;
+ // it may be in a sub-directory.
+ File jarDir = jarFile.getParentFile();
+ if (!DefaultBundleCache.getSecureAction().fileExists(jarDir))
+ {
+ if (!DefaultBundleCache.getSecureAction().mkdirs(jarDir))
+ {
+ throw new IOException("Unable to create embedded JAR directory.");
+ }
+ }
+
+ // Extract embedded JAR into its directory.
+ is = new BufferedInputStream(bundleJar.getInputStream(ze), DefaultBundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + jarPath);
+ }
+ // Copy the file.
+ DefaultBundleCache.copyStreamToFile(is, jarFile);
+ }
+ }
+ finally
+ {
+ if (bundleJar != null) bundleJar.close();
+ if (is != null) is.close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java
new file mode 100644
index 0000000..bf261d4
--- /dev/null
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/cache/SystemBundleArchive.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Map;
+
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.moduleloader.IContent;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+
+/**
+ * <p>
+ * This class represents the bundle archive of the system bundle. It is a
+ * special case that is mostly just an empty implementation, since the system
+ * bundle is not a real archive.
+ * </p>
+**/
+public class SystemBundleArchive extends DefaultBundleArchive
+{
+ private Map m_headerMap = null;
+ private BundleRevision m_revision = null;
+
+ public SystemBundleArchive()
+ {
+ m_revision = new BundleRevision() {
+
+ public Map getManifestHeader() throws Exception
+ {
+ return m_headerMap;
+ }
+
+ public IContent getContent() throws Exception
+ {
+ return null;
+ }
+
+ public IContent[] getContentPath() throws Exception
+ {
+ return null;
+ }
+
+ public String findLibrary(String libName) throws Exception
+ {
+ return null;
+ }
+
+ public void dispose() throws Exception
+ {
+ }
+ };
+ }
+
+ public long getId()
+ {
+ return 0;
+ }
+
+ public String getLocation() throws Exception
+ {
+ return FelixConstants.SYSTEM_BUNDLE_LOCATION;
+ }
+
+ public String getCurrentLocation() throws Exception
+ {
+ return null;
+ }
+
+ public void setCurrentLocation(String location) throws Exception
+ {
+ }
+
+ public int getPersistentState() throws Exception
+ {
+ return Bundle.ACTIVE;
+ }
+
+ public void setPersistentState(int state) throws Exception
+ {
+ }
+
+ public int getStartLevel() throws Exception
+ {
+ return FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
+ }
+
+ public void setStartLevel(int level) throws Exception
+ {
+ }
+
+ public File getDataFile(String fileName) throws Exception
+ {
+ return null;
+ }
+
+ public BundleActivator getActivator(IModule module)
+ throws Exception
+ {
+ return null;
+ }
+
+ public void setActivator(Object obj) throws Exception
+ {
+ }
+
+ public int getRevisionCount()
+ {
+ return 1;
+ }
+
+ public BundleRevision getRevision(int i)
+ {
+ return m_revision;
+ }
+
+ public void revise(String location, InputStream is)
+ throws Exception
+ {
+ }
+
+ public void purge() throws Exception
+ {
+ }
+
+ public void dispose() throws Exception
+ {
+ }
+
+ public Map getManifestHeader(int revision)
+ throws Exception
+ {
+ return m_headerMap;
+ }
+
+ public void setManifestHeader(Map headerMap)
+ {
+ m_headerMap = headerMap;
+ }
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Library.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Library.java
index 80bdada..3900790 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Library.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Library.java
@@ -17,13 +17,13 @@
package org.apache.felix.framework.searchpolicy;
import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.cache.BundleCache;
+import org.apache.felix.framework.cache.DefaultBundleCache;
import org.osgi.framework.Constants;
public class R4Library
{
private Logger m_logger = null;
- private BundleCache m_cache = null;
+ private DefaultBundleCache m_cache = null;
private long m_bundleId = -1;
private int m_revision = -1;
private String m_os = null;
@@ -31,7 +31,7 @@
private R4LibraryHeader m_header = null;
public R4Library(
- Logger logger, BundleCache cache, long bundleId, int revision,
+ Logger logger, DefaultBundleCache cache, long bundleId, int revision,
String os, String processor, R4LibraryHeader header)
{
m_logger = logger;
@@ -63,7 +63,7 @@
{
try {
return m_cache.getArchive(m_bundleId)
- .findLibrary(m_revision, m_header.getName());
+ .getRevision(m_revision).findLibrary(m_header.getName());
} catch (Exception ex) {
m_logger.log(Logger.LOG_ERROR, "R4Library: Error finding library.", ex);
}
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 876cb43..e751f3e 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -43,7 +43,6 @@
public static final String AUTO_START_PROP = "felix.auto.start";
public static final String EMBEDDED_EXECUTION_PROP = "felix.embedded.execution";
public static final String STRICT_OSGI_PROP = "felix.strict.osgi";
- public static final String CACHE_CLASS_PROP = "felix.cache.class";
public static final String FRAMEWORK_STARTLEVEL_PROP
= "felix.startlevel.framework";
public static final String BUNDLE_STARTLEVEL_PROP
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/shell/impl/InstallCommandImpl.java b/org.apache.felix.framework/src/main/java/org/apache/felix/shell/impl/InstallCommandImpl.java
index 82e718e..0275bca 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/shell/impl/InstallCommandImpl.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/shell/impl/InstallCommandImpl.java
@@ -17,7 +17,6 @@
package org.apache.felix.shell.impl;
import java.io.PrintStream;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.StringTokenizer;
@@ -109,15 +108,10 @@
private String absoluteLocation(String location)
{
- if (!location.endsWith(".jar"))
- {
- location = location + ".jar";
- }
- try
- {
- new URL(location);
- }
- catch (MalformedURLException ex)
+ String guess = location;
+ // If the location does not contain a ":", then try to
+ // add the base URL from the 'cd' command service.
+ if (location.indexOf(':') < 0)
{
// Try to create a valid URL using the base URL
// contained in the "cd" command service.
@@ -137,16 +131,16 @@
m_context.ungetService(ref);
}
- String theURL = baseURL + location;
+ String theURL = baseURL + guess;
new URL(theURL);
-
}
catch (Exception ex2)
{
- return null;
+ // If that fails, then just return the original.
+ return location;
}
- location = baseURL + location;
+ guess = baseURL + guess;
}
- return location;
+ return guess;
}
}
\ No newline at end of file