SLING-5591 : Race condition in installer: cached artifacts might be made unavailable

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1734096 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/installer/api/tasks/TransformationResult.java b/src/main/java/org/apache/sling/installer/api/tasks/TransformationResult.java
index 138b505..1022321 100644
--- a/src/main/java/org/apache/sling/installer/api/tasks/TransformationResult.java
+++ b/src/main/java/org/apache/sling/installer/api/tasks/TransformationResult.java
@@ -129,4 +129,11 @@
     public Version getVersion() {
         return this.version;
     }
+
+    @Override
+    public String toString() {
+        return "TransformationResult [resourceType=" + resourceType + ", id=" + id + ", version=" + version
+                + ", attributes=" + attributes + "]";
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/installer/api/tasks/package-info.java b/src/main/java/org/apache/sling/installer/api/tasks/package-info.java
index ae26ea2..0d63906 100644
--- a/src/main/java/org/apache/sling/installer/api/tasks/package-info.java
+++ b/src/main/java/org/apache/sling/installer/api/tasks/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@Version("1.3.2")
+@Version("1.3.4")
 package org.apache.sling.installer.api.tasks;
 
 import aQute.bnd.annotation.Version;
diff --git a/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java b/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java
index 0951536..33466e1 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/DefaultTransformer.java
@@ -46,6 +46,7 @@
     /**
      * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener, RetryHandler)
      */
+    @Override
     public void init(final BundleContext bctx, final ResourceChangeListener rcl, RetryHandler retryHandler) {
         // nothing to do
     }
@@ -53,6 +54,7 @@
     /**
      * @see org.apache.sling.installer.core.impl.InternalService#deactivate()
      */
+    @Override
     public void deactivate() {
         // nothing to do
     }
@@ -60,6 +62,7 @@
     /**
      * @see org.apache.sling.installer.core.impl.InternalService#getDescription()
      */
+    @Override
     public String getDescription() {
         return "Apache Sling Installer - Default Resource Transformer";
     }
@@ -67,10 +70,13 @@
     /**
      * @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource)
      */
+    @Override
     public TransformationResult[] transform(final RegisteredResource resource) {
+        logger.debug("Trying to transform {} : {}", resource, resource.getType());
         if ( resource.getType().equals(InstallableResource.TYPE_FILE) ) {
             return checkBundle(resource);
         }
+        logger.debug("Unsupported type {} : {}", resource, resource.getType());
         return null;
     }
 
@@ -79,14 +85,16 @@
      * @return
      */
     private TransformationResult[] checkBundle(final RegisteredResource resource) {
+        logger.debug("Checking headers for {}", resource);
         final Util.BundleHeaders headers = Util.readBundleHeaders(resource, logger);
+        logger.debug("Found headers for {} : {}", resource, headers);
         if ( headers != null ) {
             // check the version for validity
             boolean validVersion = true;
             try {
                 new Version(headers.version);
             } catch (final IllegalArgumentException iae) {
-                logger.info("Rejecting bundle {} from {} due to invalid version information: {}.", 
+                logger.info("Rejecting bundle {} from {} due to invalid version information: {}.",
                         new Object[] {headers.symbolicName, resource, headers.version});
                 validVersion = false;
             }
@@ -105,6 +113,7 @@
                 tr.setResourceType(InstallableResource.TYPE_BUNDLE);
                 tr.setAttributes(attr);
 
+                logger.debug("Transformed {} to {}", resource, tr);
                 return new TransformationResult[] {tr};
             }
         }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/FileDataStore.java b/src/main/java/org/apache/sling/installer/core/impl/FileDataStore.java
index 24bbfac..59e171e 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/FileDataStore.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/FileDataStore.java
@@ -61,7 +61,17 @@
     public static FileDataStore SHARED;
 
     /** Cache for url to digest mapping. */
-    private final Map<String, String> digestCache = new HashMap<String, String>();
+    private final Map<String, CacheEntry> digestCache = new HashMap<String, CacheEntry>();
+
+    private static final class CacheEntry {
+        public final File file;
+        public final String digest;
+
+        public CacheEntry(final File file, final String digest) {
+            this.file = file;
+            this.digest = digest;
+        }
+    }
 
     /**
      * Create a file util instance and detect the installer directory.
@@ -139,34 +149,35 @@
         // check if we already have this data
         if ( digest != null ) {
             synchronized ( this.digestCache ) {
-                final String storedDigest = this.digestCache.get(url);
-                if ( storedDigest != null && storedDigest.equals(digest) ) {
-                    return null;
+                final CacheEntry storedDigest = this.digestCache.get(url);
+                if ( storedDigest != null && storedDigest.digest.equals(digest) ) {
+                    return storedDigest.file;
                 }
             }
         }
         final int pos = url.lastIndexOf('/');
         final String name = url.substring(pos + 1);
         final String filename = (hint == null ? "rsrc" : hint) + '-' + name + '-' + getNextSerialNumber() + ".ser";
-        
+
         //replace special characters from the filename that are not allowed by the OS
         final String filename2 = filename.replaceAll("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+", "_"); // Windows
-      
+
         final File file = this.getDataFile(filename2);
 
         this.copyToLocalStorage(stream, file);
 
         if ( digest != null ) {
             synchronized ( this.digestCache ) {
-                this.digestCache.put(url, digest);
+                this.digestCache.put(url, new CacheEntry(file, digest));
             }
         }
+
         return file;
     }
 
-    public void updateDigestCache(final String url, final String digest) {
+    public void updateDigestCache(final String url, final File file, final String digest) {
         synchronized ( this.digestCache ) {
-            this.digestCache.put(url, digest);
+            this.digestCache.put(url, new CacheEntry(file, digest));
         }
     }
 
@@ -200,8 +211,8 @@
 
     public void removeFromDigestCache(final String url, final String digest) {
         synchronized ( this.digestCache ) {
-            final String storedDigest = this.digestCache.get(url);
-            if ( storedDigest != null && storedDigest.equals(digest) ) {
+            final CacheEntry entry = this.digestCache.get(url);
+            if ( entry != null && entry.digest.equals(digest) ) {
                 this.digestCache.remove(url);
             }
         }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/InternalResource.java b/src/main/java/org/apache/sling/installer/core/impl/InternalResource.java
index 352e0bd..1dab202 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/InternalResource.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/InternalResource.java
@@ -124,7 +124,7 @@
                     digest = resource.getDigest();
                 } else {
                     digest = FileDataStore.computeDigest(dataFile);
-                    FileDataStore.SHARED.updateDigestCache(url, digest);
+                    FileDataStore.SHARED.updateDigestCache(url, dataFile, digest);
                 }
             }
         }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/PersistentResourceList.java b/src/main/java/org/apache/sling/installer/core/impl/PersistentResourceList.java
index 8a38c34..1989b03 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/PersistentResourceList.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/PersistentResourceList.java
@@ -144,13 +144,13 @@
         for(final EntityResourceList group : this.data.values()) {
             for(final RegisteredResource rr : group.getResources()) {
                 if ( ((RegisteredResourceImpl)rr).hasDataFile() ) {
-                    FileDataStore.SHARED.updateDigestCache(rr.getURL(), rr.getDigest());
+                    FileDataStore.SHARED.updateDigestCache(rr.getURL(), ((RegisteredResourceImpl)rr).getDataFile(), rr.getDigest());
                 }
             }
         }
         for(final RegisteredResource rr : this.untransformedResources ) {
             if ( ((RegisteredResourceImpl)rr).hasDataFile() ) {
-                FileDataStore.SHARED.updateDigestCache(rr.getURL(), rr.getDigest());
+                FileDataStore.SHARED.updateDigestCache(rr.getURL(), ((RegisteredResourceImpl)rr).getDataFile(), rr.getDigest());
             }
         }
     }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/RegisteredResourceImpl.java b/src/main/java/org/apache/sling/installer/core/impl/RegisteredResourceImpl.java
index 49fc097..b158262 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/RegisteredResourceImpl.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/RegisteredResourceImpl.java
@@ -235,6 +235,10 @@
 	    return this.dataFile != null;
 	}
 
+	public File getDataFile() {
+	    return this.dataFile;
+	}
+
 	/**
 	 * Remove the data file
 	 */
@@ -259,14 +263,16 @@
 	/**
 	 * @see org.apache.sling.installer.api.tasks.RegisteredResource#getURL()
 	 */
-	public String getURL() {
+	@Override
+    public String getURL() {
 		return this.url;
 	}
 
 	/**
 	 * @see org.apache.sling.installer.api.tasks.RegisteredResource#getInputStream()
 	 */
-	public InputStream getInputStream() throws IOException {
+	@Override
+    public InputStream getInputStream() throws IOException {
 	    if ( this.dataUri != null ) {
 	        try {
     	        final URI uri = new URI(this.dataUri);
@@ -284,20 +290,23 @@
 	/**
 	 * @see org.apache.sling.installer.api.tasks.RegisteredResource#getDictionary()
 	 */
-	public Dictionary<String, Object> getDictionary() {
+	@Override
+    public Dictionary<String, Object> getDictionary() {
 		return dictionary;
 	}
 
 	/**
 	 * @see org.apache.sling.installer.api.tasks.RegisteredResource#getDigest()
 	 */
-	public String getDigest() {
+	@Override
+    public String getDigest() {
 		return digest;
 	}
 
     /**
      * @see org.apache.sling.installer.api.tasks.RegisteredResource#getType()
      */
+    @Override
     public String getType() {
         return resourceType;
     }
@@ -305,6 +314,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.RegisteredResource#getEntityId()
      */
+    @Override
     public String getEntityId() {
         return entity;
     }
@@ -312,6 +322,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#getAttribute(java.lang.String)
      */
+    @Override
     public Object getAttribute(final String key) {
         return this.attributes.get(key);
     }
@@ -319,6 +330,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#setAttribute(java.lang.String, java.lang.Object)
      */
+    @Override
     public void setAttribute(final String key, final Object value) {
         if ( value == null ) {
             this.attributes.remove(key);
@@ -330,6 +342,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.RegisteredResource#getScheme()
      */
+    @Override
     public String getScheme() {
         return urlScheme;
     }
@@ -337,6 +350,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.RegisteredResource#getPriority()
      */
+    @Override
     public int getPriority() {
         return priority;
     }
@@ -344,6 +358,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#getState()
      */
+    @Override
     public ResourceState getState() {
         return this.state;
     }
@@ -392,6 +407,7 @@
     /**
      * @see java.lang.Comparable#compareTo(java.lang.Object)
      */
+    @Override
     public int compareTo(final RegisteredResourceImpl b) {
         return compare(this, b);
     }
@@ -426,14 +442,14 @@
      */
     public static int compare(final TaskResource a, final TaskResource b) {
         int result = 0;
-        
+
         // check entity id first
         final String aId = a.getEntityId();
         final String bId = b.getEntityId();
         if(aId != null && bId != null) {
             result = aId.compareTo(bId);
         }
-        
+
         boolean hasVersion = false;
         if ( result == 0 ) {
             // compare versions
@@ -486,6 +502,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#getTemporaryAttribute(java.lang.String)
      */
+    @Override
     public Object getTemporaryAttribute(final String key) {
         if ( this.temporaryAttributes != null ) {
             return this.temporaryAttributes.get(key);
@@ -496,6 +513,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#setTemporaryAttribute(java.lang.String, java.lang.Object)
      */
+    @Override
     public void setTemporaryAttribute(final String key, final Object value) {
         if ( this.temporaryAttributes == null ) {
             this.temporaryAttributes = new HashMap<String, Object>();
@@ -572,7 +590,7 @@
                     this.removeDataFile();
                 }
                 this.dataFile = rsrc.getPrivateCopyOfFile();
-                FileDataStore.SHARED.updateDigestCache(this.url, this.digest);
+                FileDataStore.SHARED.updateDigestCache(this.url, this.dataFile, this.digest);
             }
         }
     }
@@ -626,6 +644,7 @@
     /**
      * @see org.apache.sling.installer.api.tasks.TaskResource#getVersion()
      */
+    @Override
     public Version getVersion() {
         final String vInfo = (String)this.getAttribute(Constants.BUNDLE_VERSION);
         return (vInfo == null ? null : new Version(vInfo));
diff --git a/src/main/java/org/apache/sling/installer/core/impl/ResourceData.java b/src/main/java/org/apache/sling/installer/core/impl/ResourceData.java
index 5180ebf..dc241d5 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/ResourceData.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/ResourceData.java
@@ -92,7 +92,7 @@
         if ( digest == null ) {
             digest = FileDataStore.computeDigest(this.dataFile);
         }
-        FileDataStore.SHARED.updateDigestCache(url, digest);
+        FileDataStore.SHARED.updateDigestCache(url, this.dataFile, digest);
         return digest;
 
     }
diff --git a/src/main/java/org/apache/sling/installer/core/impl/Util.java b/src/main/java/org/apache/sling/installer/core/impl/Util.java
index 4085c40..fbf5ced 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/Util.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/Util.java
@@ -91,6 +91,8 @@
                     }
                 }
             }
+        } else {
+            logger.debug("Unable to get input stream from {}", rsrc);
         }
         return result;
     }
@@ -99,6 +101,12 @@
         public String symbolicName;
         public String version;
         public String activationPolicy; // optional
+
+        @Override
+        public String toString() {
+            return "BundleHeaders [symbolicName=" + symbolicName + ", version=" + version + ", activationPolicy="
+                    + activationPolicy + "]";
+        }
     }
 
     /**
@@ -125,10 +133,17 @@
                         }
 
                         return headers;
+                    } else {
+                        logger.debug("Unable to get version from manifest : {}", resource);
                     }
+                } else {
+                    logger.debug("Unable to get symbolic name from manifest : {}", resource);
                 }
+            } else {
+                logger.debug("Unable to read manifest from : {}", resource);
             }
         } catch (final IOException ignore) {
+            logger.debug("Exception occured during processing of " + resource, ignore);
             // ignore
         }
         return null;