Add locking for the bundle cache. (FELIX-2646)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1030463 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 94c23ce..37e378c 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -579,8 +579,8 @@
 
                 // Create the bundle cache, if necessary, so that we can reload any
                 // installed bundles.
-                m_cache = (BundleCache) m_configMutableMap.get(
-                    FelixConstants.FRAMEWORK_BUNDLECACHE_IMPL);
+                m_cache = (BundleCache)
+                    m_configMutableMap.get(FelixConstants.FRAMEWORK_BUNDLECACHE_IMPL);
                 if (m_cache == null)
                 {
                        try
@@ -4387,6 +4387,10 @@
                 m_extensionManager.removeExtensions(Felix.this);
             }
 
+            // Dispose of the bundle cache.
+            m_cache.release();
+            m_cache = null;
+
             // Set the framework state to resolved.
             acquireBundleLock(Felix.this, Bundle.STOPPING);
             try
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 a9d5ad6..59644b2 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
@@ -19,6 +19,8 @@
 package org.apache.felix.framework.cache;
 
 import java.io.*;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
 import java.util.*;
 
 import org.apache.felix.framework.Logger;
@@ -72,17 +74,93 @@
     protected static transient int BUFSIZE = 4096;
     protected static transient final String CACHE_DIR_NAME = "felix-cache";
     protected static transient final String CACHE_ROOTDIR_DEFAULT = ".";
+    protected static transient final String CACHE_LOCK_NAME = "cache.lock";
     protected static transient final String BUNDLE_DIR_PREFIX = "bundle";
 
     private static final SecureAction m_secureAction = new SecureAction();
 
     private final Logger m_logger;
     private final Map m_configMap;
+    private final FileLock m_lock;
 
     public BundleCache(Logger logger, Map configMap)
+        throws Exception
     {
         m_logger = logger;
         m_configMap = configMap;
+
+        // Create the cache directory, if it does not exist.
+        File cacheDir = determineCacheDir(m_configMap);
+        if (!getSecureAction().fileExists(cacheDir))
+        {
+            if (!getSecureAction().mkdirs(cacheDir))
+            {
+                m_logger.log(
+                    Logger.LOG_ERROR,
+                    "Unable to create cache directory: " + cacheDir);
+                throw new RuntimeException("Unable to create cache directory.");
+            }
+        }
+
+        File lockFile = new File(cacheDir, CACHE_LOCK_NAME);
+        FileChannel fc = null;
+        FileOutputStream fos = null;
+        FileInputStream fis = null;
+        try
+        {
+            if (!getSecureAction().fileExists(lockFile))
+            {
+                fos = getSecureAction().getFileOutputStream(lockFile);
+                fc = fos.getChannel();
+            }
+            else
+            {
+                fis = getSecureAction().getFileInputStream(lockFile);
+                fc = fis.getChannel();
+            }
+        }
+        catch (Exception ex)
+        {
+            try
+            {
+                if (fos != null) fos.close();
+                if (fis != null) fis.close();
+                if (fc != null) fc.close();
+            }
+            catch (Exception ex2)
+            {
+                // Ignore.
+            }
+            throw new Exception("Unable to create bundle cache lock file: " + ex);
+        }
+        try
+        {
+            m_lock = fc.tryLock();
+        }
+        catch (Exception ex)
+        {
+            throw new Exception("Unable to lock bundle cache: " + ex);
+        }
+
+    }
+
+    public synchronized void release()
+    {
+        if (m_lock != null)
+        {
+            try
+            {
+                m_lock.release();
+                m_lock.channel().close();
+            }
+            catch (Exception ex)
+            {
+                // Not much we can do here, just log it.
+                m_logger.log(
+                    Logger.LOG_WARNING,
+                    "Exception releasing bundle cache.", ex);
+            }
+        }
     }
 
     /* package */ static SecureAction getSecureAction()
@@ -114,20 +192,8 @@
             // Use the default value.
         }
 
-        // Create the cache directory, if it does not exist.
-        File cacheDir = determineCacheDir(m_configMap);
-        if (!getSecureAction().fileExists(cacheDir))
-        {
-            if (!getSecureAction().mkdirs(cacheDir))
-            {
-                m_logger.log(
-                    Logger.LOG_ERROR,
-                    "Unable to create cache directory: " + cacheDir);
-                throw new RuntimeException("Unable to create cache directory.");
-            }
-        }
-
         // Create the existing bundle archives in the directory, if any exist.
+        File cacheDir = determineCacheDir(m_configMap);
         List archiveList = new ArrayList();
         File[] children = getSecureAction().listDirectory(cacheDir);
         for (int i = 0; (children != null) && (i < children.length); i++)
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index a10ccac..0c140f8 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -412,7 +412,7 @@
         }
     }
 
-    public InputStream getFileInputStream(File file) throws IOException
+    public FileInputStream getFileInputStream(File file) throws IOException
     {
         if (System.getSecurityManager() != null)
         {
@@ -420,7 +420,7 @@
             {
                 Actions actions = (Actions) m_actions.get();
                 actions.set(Actions.GET_FILE_INPUT_ACTION, file);
-                return (InputStream) AccessController.doPrivileged(actions, m_acc);
+                return (FileInputStream) AccessController.doPrivileged(actions, m_acc);
             }
             catch (PrivilegedActionException ex)
             {
@@ -437,7 +437,7 @@
         }
     }
 
-    public OutputStream getFileOutputStream(File file) throws IOException
+    public FileOutputStream getFileOutputStream(File file) throws IOException
     {
         if (System.getSecurityManager() != null)
         {
@@ -445,7 +445,7 @@
             {
                 Actions actions = (Actions) m_actions.get();
                 actions.set(Actions.GET_FILE_OUTPUT_ACTION, file);
-                return (OutputStream) AccessController.doPrivileged(actions, m_acc);
+                return (FileOutputStream) AccessController.doPrivileged(actions, m_acc);
             }
             catch (PrivilegedActionException ex)
             {