[FELIX-4566] Consistency in PersistenceManager and Cache is not guaranteed

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1622684 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configadmin/src/main/java/org/apache/felix/cm/impl/CachingPersistenceManagerProxy.java b/configadmin/src/main/java/org/apache/felix/cm/impl/CachingPersistenceManagerProxy.java
index 460b813..7f3225f 100644
--- a/configadmin/src/main/java/org/apache/felix/cm/impl/CachingPersistenceManagerProxy.java
+++ b/configadmin/src/main/java/org/apache/felix/cm/impl/CachingPersistenceManagerProxy.java
@@ -20,10 +20,16 @@
 
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Vector;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.apache.felix.cm.PersistenceManager;
 import org.osgi.framework.Constants;
@@ -37,13 +43,15 @@
  */
 class CachingPersistenceManagerProxy implements PersistenceManager
 {
-
     /** the actual PersistenceManager */
     private final PersistenceManager pm;
 
     /** cached dictionaries */
     private final Hashtable cache;
 
+    /** protecting lock */
+    private final ReadWriteLock globalLock = new ReentrantReadWriteLock();
+
     /**
      * Indicates whether the getDictionaries method has already been called
      * and the cache is complete with respect to the contents of the underlying
@@ -70,8 +78,14 @@
      */
     public void delete( String pid ) throws IOException
     {
-        cache.remove( pid );
-        pm.delete( pid );
+        Lock lock = globalLock.writeLock();
+        try {
+            lock.lock();
+            cache.remove( pid );
+            pm.delete(pid);
+        } finally {
+            lock.unlock();
+        }
     }
 
 
@@ -82,7 +96,13 @@
      */
     public boolean exists( String pid )
     {
-        return cache.containsKey( pid ) || pm.exists( pid );
+        Lock lock = globalLock.readLock();
+        try {
+            lock.lock();
+            return cache.containsKey( pid ) || ( !fullyLoaded && pm.exists( pid ) );
+        } finally {
+            lock.unlock();
+        }
     }
 
 
@@ -98,39 +118,42 @@
      */
     public Enumeration getDictionaries() throws IOException
     {
-        // if not fully loaded, call back to the underlying persistence
-        // manager and cach all dictionaries whose service.pid is set
-        if ( !fullyLoaded )
-        {
-            Enumeration fromPm = pm.getDictionaries();
-            while ( fromPm.hasMoreElements() )
+        Lock lock = globalLock.readLock();
+        try {
+            lock.lock();
+            // if not fully loaded, call back to the underlying persistence
+            // manager and cach all dictionaries whose service.pid is set
+            if ( !fullyLoaded )
             {
-                Dictionary next = ( Dictionary ) fromPm.nextElement();
-                String pid = ( String ) next.get( Constants.SERVICE_PID );
-                if ( pid != null )
+                lock.unlock();
+                lock = globalLock.writeLock();
+                lock.lock();
+                if ( !fullyLoaded )
                 {
-                    cache.put( pid, next );
+                    Enumeration fromPm = pm.getDictionaries();
+                    while (fromPm.hasMoreElements())
+                    {
+                        Dictionary next = (Dictionary) fromPm.nextElement();
+                        String pid = (String) next.get(Constants.SERVICE_PID);
+                        if (pid != null)
+                        {
+                            cache.put(pid, next);
+                        }
+                    }
+                    fullyLoaded = true;
                 }
             }
-            fullyLoaded = true;
+
+            // Deep copy the configuration to avoid any threading issue
+            Vector configs = new Vector();
+            for (Object o : cache.values())
+            {
+                configs.add( copy( ( Dictionary ) o ) );
+            }
+            return configs.elements();
+        } finally {
+            lock.unlock();
         }
-
-        return new Enumeration()
-        {
-            final Enumeration base = cache.elements();
-
-
-            public boolean hasMoreElements()
-            {
-                return base.hasMoreElements();
-            }
-
-
-            public Object nextElement()
-            {
-                return copy( ( Dictionary ) base.nextElement() );
-            }
-        };
     }
 
 
@@ -146,16 +169,26 @@
      */
     public Dictionary load( String pid ) throws IOException
     {
-        Dictionary loaded = ( Dictionary ) cache.get( pid );
-        if ( loaded == null )
-        {
-            loaded = pm.load( pid );
-            if ( loaded != null )
+        Lock lock = globalLock.readLock();
+        try {
+            lock.lock();
+            Dictionary loaded = ( Dictionary ) cache.get( pid );
+            if ( loaded == null && !fullyLoaded )
             {
-                cache.put( pid, loaded );
+                lock.unlock();
+                lock = globalLock.writeLock();
+                lock.lock();
+                loaded = ( Dictionary ) cache.get( pid );
+                if ( loaded == null )
+                {
+                    loaded = pm.load(pid);
+                    cache.put(pid, loaded);
+                }
             }
+            return copy( loaded );
+        } finally {
+            lock.unlock();
         }
-        return copy( loaded );
     }
 
 
@@ -170,8 +203,14 @@
      */
     public void store( String pid, Dictionary properties ) throws IOException
     {
-        pm.store( pid, properties );
-        cache.put( pid, copy( properties ) );
+        Lock lock = globalLock.writeLock();
+        try {
+            lock.lock();
+            pm.store( pid, properties );
+            cache.put( pid, copy( properties ) );
+        } finally {
+            lock.unlock();
+        }
     }