Added support for persistent last used bundle ID to avoid re-use of
bundle identifiers. (FELIX-339)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@567122 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index b3d6393..6421995 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -58,14 +58,14 @@
// Maps a bundle location to a bundle location;
// used to reserve a location when installing a bundle.
- private Map m_installRequestMap = null;
+ private Map m_installRequestMap = new HashMap();
// This lock must be acquired to modify m_installRequestMap;
// to help avoid deadlock this lock as priority 1 and should
// be acquired before locks with lower priority.
private Object[] m_installRequestLock_Priority1 = new Object[0];
// Maps a bundle location to a bundle.
- private HashMap m_installedBundleMap = null;
+ private HashMap m_installedBundleMap = new HashMap();
// This lock must be acquired to modify m_installedBundleMap;
// to help avoid deadlock this lock as priority 2 and should
// be acquired before locks with lower priority.
@@ -229,25 +229,42 @@
// will be set below after the system bundle is created.
m_logger = new Logger((String) m_configMutableMap.get(FelixConstants.LOG_LEVEL_PROP));
- // Initialize other member variables.
- m_activeStartLevel = FelixConstants.FRAMEWORK_INACTIVE_STARTLEVEL;
- m_installRequestMap = new HashMap();
- m_installedBundleMap = new HashMap();
- m_uninstalledBundles = null;
- m_cache = null;
- m_systemBundleInfo = null;
- m_nextId = 1L;
- m_dispatcher = null;
-
// Initialize framework properties.
initializeFrameworkProperties();
+ // Create the bundle cache since we need it for the system bundle
+ // archive, which in turn is needed by the system bundle info,
+ // which we need to keep track of the framework state.
+ try
+ {
+ m_cache = new BundleCache(m_logger, m_configMap);
+ }
+ catch (Exception ex)
+ {
+ System.err.println("Error creating bundle cache:");
+ ex.printStackTrace();
+
+ // Only shutdown the JVM if the framework is running stand-alone.
+ String embedded = (String) m_configMap.get(
+ FelixConstants.EMBEDDED_EXECUTION_PROP);
+ boolean isEmbedded = (embedded == null)
+ ? false : embedded.equals("true");
+ if (!isEmbedded)
+ {
+ m_secureAction.exit(-1);
+ }
+ else
+ {
+ throw new RuntimeException(ex.toString());
+ }
+ }
+
// Create the module factory and add the system bundle
// module to it, since we need the system bundle info
// object to keep track of the framework state.
m_factory = new ModuleFactoryImpl(m_logger);
m_systemBundleInfo = new BundleInfo(
- m_logger, new SystemBundleArchive(), null);
+ m_logger, new SystemBundleArchive(m_cache), null);
m_extensionManager =
new ExtensionManager(m_logger, m_configMap, m_systemBundleInfo);
m_systemBundleInfo.addModule(
@@ -577,30 +594,6 @@
}
});
- try
- {
- m_cache = new BundleCache(m_logger, m_configMap);
- }
- catch (Exception ex)
- {
- System.err.println("Error creating bundle cache:");
- ex.printStackTrace();
-
- // Only shutdown the JVM if the framework is running stand-alone.
- String embedded = (String) m_configMap.get(
- FelixConstants.EMBEDDED_EXECUTION_PROP);
- boolean isEmbedded = (embedded == null)
- ? false : embedded.equals("true");
- if (!isEmbedded)
- {
- m_secureAction.exit(-1);
- }
- else
- {
- throw new RuntimeException(ex.toString());
- }
- }
-
// Create search policy for module loader.
m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
@@ -680,6 +673,7 @@
m_installedBundleMap.put(
m_systemBundleInfo.getLocation(), this);
+
// Manually resolve the System Bundle, which will cause its
// state to be set to RESOLVED.
try
@@ -726,7 +720,8 @@
{
m_logger.log(
Logger.LOG_ERROR,
- "Unable to list saved bundles: " + ex, ex);
+ "Unable to list saved bundles.",
+ ex);
archives = null;
}
@@ -737,9 +732,9 @@
{
try
{
- // Make sure our id generator is not going to overlap.
- // TODO: FRAMEWORK - This is not correct since it may lead to re-used
- // ids, which is not okay according to OSGi.
+ // Keep track of the max bundle ID currently in use since we
+ // will need to use this as our next bundle ID value if the
+ // persisted value cannot be read.
m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
// It is possible that a bundle in the cache was previously
@@ -783,6 +778,12 @@
}
}
+ // Now that we have loaded all cached bundles and have determined the
+ // max bundle ID of cached bundles, we need to try to load the next
+ // bundle ID from persistent storage. In case of failure, we should
+ // keep the max value.
+ m_nextId = Math.max(m_nextId, loadNextId());
+
// Get the framework's default start level.
int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
String s = (String) m_configMap.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
@@ -3763,11 +3764,98 @@
/**
* Generated the next valid bundle identifier.
**/
+ private long loadNextId()
+ {
+ synchronized (m_nextIdLock)
+ {
+ // Read persisted next bundle identifier.
+ InputStream is = null;
+ BufferedReader br = null;
+ try
+ {
+ File file = m_cache.getSystemBundleDataFile("bundle.id");
+ is = m_secureAction.getFileInputStream(file);
+ br = new BufferedReader(new InputStreamReader(is));
+ return Long.parseLong(br.readLine());
+ }
+ catch (FileNotFoundException ex)
+ {
+ // Ignore this case because we assume that this is the
+ // initial startup of the framework and therefore the
+ // file does not exist yet.
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Unable to initialize next bundle identifier from persistent storage.",
+ ex);
+ }
+ finally
+ {
+ try
+ {
+ if (br != null) br.close();
+ if (is != null) is.close();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Unable to close next bundle identifier file.",
+ ex);
+ }
+ }
+ }
+
+ return -1;
+ }
+
private long getNextId()
{
synchronized (m_nextIdLock)
{
- return m_nextId++;
+ // Save the current id.
+ long id = m_nextId;
+
+ // Increment the next id.
+ m_nextId++;
+
+ // Write the bundle state.
+ OutputStream os = null;
+ BufferedWriter bw = null;
+ try
+ {
+ File file = m_cache.getSystemBundleDataFile("bundle.id");
+ os = m_secureAction.getFileOutputStream(file);
+ bw = new BufferedWriter(new OutputStreamWriter(os));
+ String s = Long.toString(m_nextId);
+ bw.write(s, 0, s.length());
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Unable to save next bundle identifier to persistent storage.",
+ ex);
+ }
+ finally
+ {
+ try
+ {
+ if (bw != null) bw.close();
+ if (os != null) os.close();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Unable to close next bundle identifier file.",
+ ex);
+ }
+ }
+
+ return id;
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
index 402ba62..2452fce 100644
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
@@ -40,11 +40,14 @@
**/
public class SystemBundleArchive extends BundleArchive
{
+ private BundleCache m_cache;
private Map m_headerMap = new StringMap(false);
- private BundleRevision m_revision = null;
+ private BundleRevision m_revision;
- public SystemBundleArchive()
+ public SystemBundleArchive(BundleCache cache)
{
+ m_cache = cache;
+
try
{
m_revision = new BundleRevision(null, null, null) {
@@ -124,7 +127,7 @@
public File getDataFile(String fileName) throws Exception
{
- return null;
+ return m_cache.getSystemBundleDataFile(fileName);
}
public BundleActivator getActivator(IModule module)
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 908fe20..43692ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -191,6 +191,43 @@
}
}
+ /**
+ * Provides the system bundle access to its private storage area; this
+ * special case is necessary since the system bundle is not really a
+ * bundle and therefore must be treated in a special way.
+ * @param fileName the name of the file in the system bundle's private area.
+ * @return a <tt>File</tt> object corresponding to the specified file name.
+ * @throws Exception if any error occurs.
+ **/
+ public synchronized File getSystemBundleDataFile(String fileName) throws Exception
+ {
+ // Make sure system bundle directory exists.
+ File sbDir = new File(m_profileDir, BUNDLE_DIR_PREFIX + Long.toString(0));
+
+ // If the system bundle directory exists, then we don't
+ // need to initialize since it has already been done.
+ if (!getSecureAction().fileExists(sbDir))
+ {
+ // Create system bundle directory, if it does not exist.
+ if (!getSecureAction().mkdirs(sbDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ getClass().getName() + ": Unable to create system bundle directory.");
+ throw new IOException("Unable to create system bundle directory.");
+ }
+ }
+
+ // Do some sanity checking.
+ if ((fileName.length() > 0) && (fileName.charAt(0) == File.separatorChar))
+ throw new IllegalArgumentException("The data file path must be relative, not absolute.");
+ else if (fileName.indexOf("..") >= 0)
+ throw new IllegalArgumentException("The data file path cannot contain a reference to the \"..\" directory.");
+
+ // Return the data file.
+ return new File(sbDir, fileName);
+ }
+
//
// Static file-related utility methods.
//
@@ -323,8 +360,10 @@
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))
+ // Ignore directories that aren't bundle directories or
+ // is the system bundle directory.
+ if (children[i].getName().startsWith(BUNDLE_DIR_PREFIX) &&
+ !children[i].getName().equals(BUNDLE_DIR_PREFIX + Long.toString(0)))
{
// Recreate the bundle archive.
try
@@ -343,4 +382,4 @@
m_archives = (BundleArchive[])
archiveList.toArray(new BundleArchive[archiveList.size()]);
}
-}
+}
\ No newline at end of file