Merge branch 'master' into close-jarfile
diff --git a/.gitignore b/.gitignore
index 671b0cd..2b6b15e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 */src/main/resources/META-INF/MANIFEST.MF
 */src/test/resources/META-INF/TEST.MF
+junit*.properties
 target
 integration-repo
 ivy-cache
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/ArtifactFSEntry.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/ArtifactFSEntry.java
index 5682d00..ade0847 100644
--- a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/ArtifactFSEntry.java
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/ArtifactFSEntry.java
@@ -11,6 +11,7 @@
 
 package org.eclipse.virgo.kernel.artifact.fs;
 
+import java.io.Closeable;
 import java.io.InputStream;
 import java.io.OutputStream;
 
@@ -24,7 +25,7 @@
  * 
  * @see ArtifactFS
  */
-public interface ArtifactFSEntry {
+public interface ArtifactFSEntry extends Closeable {
 
     /**
      * Gets the path of this entry, relative to the root {@link ArtifactFS}
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/StandardArtifactFSFactory.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/StandardArtifactFSFactory.java
index 7c72be8..9cbfc73 100644
--- a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/StandardArtifactFSFactory.java
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/StandardArtifactFSFactory.java
@@ -12,12 +12,18 @@
 package org.eclipse.virgo.kernel.artifact.fs;
 
 import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
 
 import org.eclipse.virgo.kernel.artifact.fs.internal.DirectoryArtifactFS;
 import org.eclipse.virgo.kernel.artifact.fs.internal.FileArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.internal.JarFileArtifactFS;
 
 
 public final class StandardArtifactFSFactory implements ArtifactFSFactory {
+    
+    private static final List<String> JAR_EXTENSIONS = Arrays.asList("jar", "war");
 
     /**
      * {@inheritDoc}
@@ -26,7 +32,18 @@
         if (file.isDirectory()) {
             return new DirectoryArtifactFS(file);
         }
-        return new FileArtifactFS(file);
+        return looksLikeAJar(file.getName()) ? new JarFileArtifactFS(file) : new FileArtifactFS(file);
     }
+    
+    private boolean looksLikeAJar(String name) {
+        String fileName = name.toLowerCase(Locale.ENGLISH);
+
+        int dotLocation = fileName.lastIndexOf('.');
+        if (dotLocation == -1) {
+            return false;
+        }
+        return JAR_EXTENSIONS.contains(fileName.substring(dotLocation + 1));
+    }
+
 
 }
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFS.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFS.java
index 5363753..5641faf 100644
--- a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFS.java
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFS.java
@@ -49,7 +49,7 @@
         return new FileArtifactFSEntry(this.file.getParentFile(), this.file);
     }
 
-    public File getFile() {
+    public final File getFile() {
         return this.file;
     }
 
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFSEntry.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFSEntry.java
index e6eaf9e..8a9bc8c 100644
--- a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFSEntry.java
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/FileArtifactFSEntry.java
@@ -15,6 +15,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
@@ -121,4 +122,7 @@
         return this.file.exists();
     }
 
+    public void close() throws IOException {
+    }
+
 }
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFS.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFS.java
new file mode 100644
index 0000000..5b4cf97
--- /dev/null
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFS.java
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the Eclipse Virgo project.
+ *
+ * Copyright (c) 2012 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    VMware Inc. - initial contribution
+ */
+
+package org.eclipse.virgo.kernel.artifact.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.eclipse.virgo.util.common.StringUtils;
+
+/**
+ * {@link JarFileArtifactFS} is an {@link ArtifactFS} implementation for JAR files.
+ * 
+ * <strong>Concurrent Semantics</strong><br />
+ * 
+ * Thread safe
+ */
+public final class JarFileArtifactFS extends FileArtifactFS implements ArtifactFS {
+
+    /**
+     * Constructs a new {@link JarFileArtifactFS} for the given file which is assumed to be in JAR format.
+     * 
+     * @param file a JAR file
+     */
+    public JarFileArtifactFS(File file) {
+        super(file);
+    }
+
+    /**
+     * {@InheritDoc}
+     */
+    @Override
+    public ArtifactFSEntry getEntry(String name) {
+        if (!StringUtils.hasText(name)) {
+            return super.getEntry(name);
+        } else {
+            try {
+                return new JarFileArtifactFSEntry(getFile(), name);
+            } catch (IOException e) {
+                // Preserve compatibility with existing interface
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSEntry.java b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSEntry.java
new file mode 100644
index 0000000..58e0664
--- /dev/null
+++ b/org.eclipse.virgo.kernel.artifact/src/main/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSEntry.java
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the Eclipse Virgo project.
+ *
+ * Copyright (c) 2012 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    VMware Inc. - initial contribution
+ */
+
+package org.eclipse.virgo.kernel.artifact.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+
+/**
+ * {@link JarFileArtifactFSEntry} is an {@link ArtifactFSEntry} implementation for JAR file entries.
+ * 
+ * <strong>Concurrent Semantics</strong><br />
+ * 
+ * Thread safe
+ */
+final class JarFileArtifactFSEntry implements ArtifactFSEntry {
+
+    private final String entryName;
+
+    private final JarFile jarFile;
+
+    private final ZipEntry zipEntry;
+
+    /**
+     * Constructs a new {@link JarFileArtifactFSEntry} for the given file which is assumed to be in JAR format and the
+     * given entry name.
+     * 
+     * @param file a JAR file
+     * @param entryName the name of an entry
+     * @throws IOException if the entry cannot be created
+     */
+    JarFileArtifactFSEntry(File file, String entryName) throws IOException {
+        this(new JarFile(file), entryName);
+    }
+
+    private JarFileArtifactFSEntry(JarFile jarFile, String entryName) {
+        this.entryName = entryName;
+        this.jarFile = jarFile;
+        this.zipEntry = jarFile.getEntry(entryName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPath() {
+        return this.entryName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getName() {
+        String filePath = removeTrailingSlash();
+        int lastDir = filePath.lastIndexOf("/");
+        return filePath.substring(lastDir + 1);
+    }
+
+    private String removeTrailingSlash() {
+        return this.entryName.endsWith("/") ? this.entryName.substring(0, this.entryName.length() - 1) : this.entryName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean delete() {
+        throw new UnsupportedOperationException("This ArtifactFSEntry is a member of a JAR file. Deleting it is unsupported");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isDirectory() {
+        return exists() ? this.zipEntry.isDirectory() : false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream getInputStream() {
+        if (!exists()) {
+            throw new UnsupportedOperationException("Cannot open an input stream for a non-existent entry");
+        }
+
+        if (isDirectory()) {
+            throw new UnsupportedOperationException("Cannot open an input stream for a directory");
+        }
+        
+        try {
+            return this.jarFile.getInputStream(this.zipEntry);
+        } catch (IOException e) {
+            // Preserve compatibility with current interface.
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public OutputStream getOutputStream() {
+        throw new UnsupportedOperationException("This ArtifactFSEntry is a member of a JAR file. Writing it is unsupported");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ArtifactFSEntry[] getChildren() {
+        if (!isDirectory()) {
+            throw new UnsupportedOperationException("Cannot get children of a non-directory entry");
+        }
+        Set<ArtifactFSEntry> children = new HashSet<ArtifactFSEntry>();
+        if (exists()) {
+            Enumeration<JarEntry> entries = this.jarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                String childEntry = entry.getName();
+                if (childEntry.length() > this.entryName.length() && childEntry.startsWith(this.entryName)) {
+                    children.add(new JarFileArtifactFSEntry(this.jarFile, childEntry));
+                }
+            }
+        }
+        return children.toArray(new ArtifactFSEntry[children.size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ArtifactFS getArtifactFS() {
+        throw new UnsupportedOperationException("getArtifactFS method not supported by JarFileArtifactFSEntry");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists() {
+        return hasEntry(this.jarFile, this.entryName);
+    }
+
+    private static boolean hasEntry(JarFile jarFile, String entryName) {
+        Enumeration<JarEntry> entries = jarFile.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry entry = entries.nextElement();
+            String childEntry = entry.getName();
+            if (entryName.equals(childEntry)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void close() throws IOException {
+        this.jarFile.close();
+    }
+
+}
diff --git a/org.eclipse.virgo.kernel.artifact/src/test/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSTests.java b/org.eclipse.virgo.kernel.artifact/src/test/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSTests.java
new file mode 100644
index 0000000..43ed948
--- /dev/null
+++ b/org.eclipse.virgo.kernel.artifact/src/test/java/org/eclipse/virgo/kernel/artifact/fs/internal/JarFileArtifactFSTests.java
@@ -0,0 +1,215 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 VMware Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   VMware Inc. - initial contribution
+ *******************************************************************************/
+
+package org.eclipse.virgo.kernel.artifact.fs.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Scanner;
+
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
+import org.junit.Test;
+
+public class JarFileArtifactFSTests {
+
+    private final FileArtifactFS artifactFS = new JarFileArtifactFS(new File("src/test/resources/artifacts/simple.jar"));
+
+    @Test(expected = IllegalArgumentException.class)
+    public void constructorDirectory() {
+        new JarFileArtifactFS(new File("target"));
+    }
+
+    /**
+     * This usage is not expected, but is tested to ensure predictable behaviour.
+     */
+    @Test
+    public void constructorNonJarFile() {
+        ArtifactFS fileArtifactFS = new JarFileArtifactFS(new File("src/test/resources/properties/foo.properties"));
+        File file = fileArtifactFS.getFile();
+        assertEquals("foo.properties", file.getName());
+        ArtifactFSEntry entry = fileArtifactFS.getEntry("");
+        assertTrue(entry instanceof FileArtifactFSEntry);
+        assertFalse(entry instanceof JarFileArtifactFSEntry);
+        try {
+            fileArtifactFS.getEntry("blah");
+            fail("Attempt to get bad entry unexpectedly succeeded");
+        } catch (RuntimeException _) {
+        }
+    }
+
+    /**
+     * Just in case manifests are somehow special cased in the implementation.
+     */
+    @Test
+    public void getManifestEntry() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("META-INF/MANIFEST.MF");
+        InputStream inputStream = entry.getInputStream();
+        String manifest = new Scanner(inputStream).useDelimiter("\\A").next();
+        assertEquals(
+            "Manifest-Version: 1.0\nCreated-By: 1.6.0_07 (Apple Inc.)\nBundle-Name: test\nBundle-SymbolicName: test\nBundle-Version: 0.0.0\n\n",
+            manifest);
+        assertEquals("MANIFEST.MF", entry.getName());
+        assertEquals("META-INF/MANIFEST.MF", entry.getPath());
+    }
+
+    @Test
+    public void getNormalEntry() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/rawfile");
+        InputStream inputStream = entry.getInputStream();
+        String rawfile = new Scanner(inputStream).useDelimiter("\\A").next();
+        assertEquals("rawfile", rawfile);
+        assertFalse(entry.isDirectory());
+        assertEquals("rawfile", entry.getName());
+        assertEquals("test/rawfile", entry.getPath());
+        assertTrue(entry.exists());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getOutputStream() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/rawfile");
+        entry.getOutputStream();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void delete() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/rawfile");
+        entry.delete();
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getEntryArtifactFS() {
+        this.artifactFS.getFile();
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/rawfile");
+        entry.getArtifactFS();
+    }
+
+    @Test
+    public void getDirectoryEntry() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/");
+        assertTrue(entry.isDirectory());
+        assertEquals("test", entry.getName());
+        assertEquals("test/", entry.getPath());
+        assertTrue(entry.exists());
+    }
+    
+    @Test(expected = UnsupportedOperationException.class)
+    public void getDirectoryInputStream() throws IOException {
+       ArtifactFSEntry entry = this.artifactFS.getEntry("test/");
+       entry.getInputStream();
+    }
+
+    @Test
+    public void getDirectoryChildren() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/");
+        ArtifactFSEntry[] children = entry.getChildren();
+        assertEquals(1, children.length);
+        assertEquals("rawfile", children[0].getName());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getFileChildren() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("test/rawfile");
+        ArtifactFSEntry[] children = entry.getChildren();
+        assertEquals(0, children.length);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getChildrenOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        ArtifactFSEntry[] children = entry.getChildren();
+        assertEquals(0, children.length);
+    }
+    
+    @Test
+    public void getPathOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        assertEquals("x/nosuch", entry.getPath());
+    }
+    
+    @Test
+    public void getNameOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        assertEquals("nosuch", entry.getName());
+    }
+
+    @Test
+    public void isDirectoryOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        assertFalse(entry.isDirectory());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getInputStreamOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        entry.getInputStream();
+    }
+    
+    @Test
+    public void getExistenceOfNonExistentFile() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch");
+        assertFalse(entry.exists());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void getChildrenOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        ArtifactFSEntry[] children = entry.getChildren();
+        assertEquals(0, children.length);
+    }
+    
+    @Test
+    public void getPathOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        assertEquals("x/nosuch/", entry.getPath());
+    }
+    
+    @Test
+    public void getNameOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        assertEquals("nosuch", entry.getName());
+    }
+
+    @Test
+    public void isDirectoryOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        assertFalse(entry.isDirectory());
+    }
+    
+    @Test(expected = UnsupportedOperationException.class)
+    public void getInputStreamOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        entry.getInputStream();
+    }
+    
+    @Test
+    public void getExistenceOfNonExistentDirectory() {
+        ArtifactFSEntry entry = this.artifactFS.getEntry("x/nosuch/");
+        assertFalse(entry.exists());
+    }
+
+    @Test
+    public void getEntryNull() {
+        assertTrue(this.artifactFS.getEntry(null) instanceof FileArtifactFSEntry);
+    }
+
+    @Test
+    public void getFile() {
+        File file = this.artifactFS.getFile();
+        assertEquals("simple.jar", file.getName());
+    }
+}
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java
index 525d174..2a35058 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ConfigLifecycleEngine.java
@@ -21,6 +21,7 @@
 import org.slf4j.LoggerFactory;
 
 import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
 import org.eclipse.virgo.kernel.deployer.config.ConfigurationDeployer;
 import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity;
 import org.eclipse.virgo.util.io.IOUtils;
@@ -66,11 +67,14 @@
 
     private void updateConfiguration(ArtifactIdentity artifactIdentity, ArtifactFS artifactFS) throws IOException {
         InputStream inputStream = null;
+        ArtifactFSEntry entry = null;
         try {
-            inputStream = artifactFS.getEntry("").getInputStream();
+            entry = artifactFS.getEntry("");
+            inputStream = entry.getInputStream();
             configurationDeployer.publishConfiguration(artifactIdentity.getName(), getProperties(inputStream));
         } finally {
             IOUtils.closeQuietly(inputStream);
+            IOUtils.closeQuietly(entry);
         }
     }
 
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java
index d0bb3f3..214e33d 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ParPlanInstallArtifact.java
@@ -34,6 +34,7 @@
 import org.eclipse.virgo.kernel.shim.scope.ScopeFactory;
 import org.eclipse.virgo.medic.eventlog.EventLogger;
 import org.eclipse.virgo.util.common.GraphNode;
+import org.eclipse.virgo.util.io.IOUtils;
 import org.eclipse.virgo.util.math.OrderedPair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -96,6 +97,7 @@
 
         ArtifactFSEntry entry = artifactFS.getEntry("/");
         ArtifactFSEntry[] children = entry.getChildren();
+        IOUtils.closeQuietly(entry);
         if (children.length == 0) {
             throw new DeploymentException("Failed to find child artifacts in par " + artifactFS);
         }
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactGraphFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactGraphFactory.java
index fa77311..5cc38d7 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactGraphFactory.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/PlanInstallArtifactGraphFactory.java
@@ -19,6 +19,7 @@
 import java.util.Locale;
 import java.util.Map;
 
+import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
 import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor;
 import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor.Provisioning;
 import org.eclipse.virgo.kernel.artifact.plan.PlanReader;
@@ -107,12 +108,15 @@
      */
     private PlanDescriptor getPlanDescriptor(ArtifactStorage artifactStorage) throws DeploymentException {
         InputStream in = null;
+        ArtifactFSEntry entry = null;
         try {
-            in = artifactStorage.getArtifactFS().getEntry("").getInputStream();
+            entry = artifactStorage.getArtifactFS().getEntry("");
+            in = entry.getInputStream();
             PlanDescriptor planDescriptor = new PlanReader().read(in);
             return planDescriptor;
         } finally {
             IOUtils.closeQuietly(in);
+            IOUtils.closeQuietly(entry);
         }
     }
 
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java
index 1865bb5..cfe0e19 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/ServiceScoper.java
@@ -28,7 +28,6 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.util.xml.XmlValidationModeDetector;
 
-
 import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
 import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
 import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
@@ -92,12 +91,16 @@
         List<ArtifactFSEntry> configFiles = new ArrayList<ArtifactFSEntry>();
 
         ArtifactFSEntry entry = bundleData.getEntry(SPRING_CONFIG_DIR);
-        if (entry.exists()) {
-            try {
-                configFiles.addAll(findConfigFiles(bundleData, entry));
-            } catch (IOException e) {
-                throw new DeploymentException("Unable to read Spring config files.", e);
+        try {
+            if (entry.exists()) {
+                try {
+                    configFiles.addAll(findConfigFiles(bundleData, entry));
+                } catch (IOException e) {
+                    throw new DeploymentException("Unable to read Spring config files.", e);
+                }
             }
+        } finally {
+            IOUtils.closeQuietly(entry);
         }
 
         return configFiles;
@@ -189,6 +192,7 @@
             throw new DeploymentException("Error reading MANIFEST.MF from '" + compositeArtifactFS + "'", ex);
         } finally {
             IOUtils.closeQuietly(reader);
+            IOUtils.closeQuietly(entry);
         }
     }
 }
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java
index 013746e..6ebee05 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/StandardArtifactStorage.java
@@ -29,7 +29,7 @@
 
 final class StandardArtifactStorage implements ArtifactStorage {
     
-    private static final List<String> JAR_EXTENSIONS = Arrays.asList("jar", "war", "par", "zip");
+    private static final List<String> UNPACK_EXTENSIONS = Arrays.asList("par", "zip");
 
     private final PathReference sourcePathReference;
 
@@ -82,7 +82,7 @@
         synchronized (this.monitor) {
             stashContent();
             if (normalizedSourcePathReference != null && !normalizedSourcePathReference.isDirectory()
-                && looksLikeAJar(normalizedSourcePathReference.getName())) {
+                && needsUnpacking(normalizedSourcePathReference.getName())) {
                 try {
                     JarUtils.unpackTo(normalizedSourcePathReference, this.baseStagingPathReference);
                 } catch (IOException e) {
@@ -98,14 +98,14 @@
         }
     }
 
-    private boolean looksLikeAJar(String name) {
+    private boolean needsUnpacking(String name) {
         String fileName = name.toLowerCase(Locale.ENGLISH);
 
         int dotLocation = fileName.lastIndexOf('.');
         if (dotLocation == -1) {
             return false;
         }
-        return JAR_EXTENSIONS.contains(fileName.substring(dotLocation + 1));
+        return UNPACK_EXTENSIONS.contains(fileName.substring(dotLocation + 1));
     }
 
     private void stashContent() {
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java
index e484f02..cb62426 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/BundleInstallArtifactFactory.java
@@ -19,7 +19,6 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Version;
 
-
 import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
 import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
 import org.eclipse.virgo.kernel.deployer.core.DeploymentException;
@@ -54,9 +53,9 @@
     private final InstallArtifactRefreshHandler refreshHandler;
 
     private final BundleDriverFactory bundleDriverFactory;
-    
+
     private final EventLogger eventLogger;
-    
+
     private final ArtifactIdentityDeterminer identityDeterminer;
 
     BundleInstallArtifactFactory(BundleContext kernelBundleContext, InstallArtifactRefreshHandler refreshHandler,
@@ -68,18 +67,20 @@
         this.identityDeterminer = identityDeterminer;
     }
 
-    BundleInstallArtifact createBundleInstallArtifact(ArtifactIdentity identity, ArtifactStorage artifactStorage, String repositoryName) throws DeploymentException {
+    BundleInstallArtifact createBundleInstallArtifact(ArtifactIdentity identity, ArtifactStorage artifactStorage, String repositoryName)
+        throws DeploymentException {
 
         ArtifactStateMonitor artifactStateMonitor = new StandardArtifactStateMonitor(this.kernelBundleContext);
 
         StandardBundleDriver bundleDriver = this.bundleDriverFactory.createBundleDriver(identity, artifactStateMonitor);
 
-        BundleManifest bundleManifest = retrieveArtifactFSManifest(artifactStorage.getArtifactFS());        
+        BundleManifest bundleManifest = retrieveArtifactFSManifest(artifactStorage.getArtifactFS());
 
-        StandardBundleInstallArtifact bundleInstallArtifact = new StandardBundleInstallArtifact(identity, bundleManifest, artifactStorage, bundleDriver, artifactStateMonitor, this.refreshHandler, repositoryName, this.eventLogger, this.identityDeterminer);
+        StandardBundleInstallArtifact bundleInstallArtifact = new StandardBundleInstallArtifact(identity, bundleManifest, artifactStorage,
+            bundleDriver, artifactStateMonitor, this.refreshHandler, repositoryName, this.eventLogger, this.identityDeterminer);
 
-        //TODO: need to set identity version from bundleManifest etc. Best to use supertype method.
-        
+        // TODO: need to set identity version from bundleManifest etc. Best to use supertype method.
+
         bundleDriver.setInstallArtifact(bundleInstallArtifact);
 
         return bundleInstallArtifact;
@@ -87,19 +88,23 @@
 
     private BundleManifest retrieveArtifactFSManifest(ArtifactFS artifactFS) throws DeploymentException {
         ArtifactFSEntry manifestEntry = artifactFS.getEntry(JarFile.MANIFEST_NAME);
-        if (manifestEntry != null && manifestEntry.exists()) {
-            try {
-                Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
+        try {
+            if (manifestEntry != null && manifestEntry.exists()) {
                 try {
-                    return BundleManifestFactory.createBundleManifest(manifestReader);
-                } finally {
-                    IOUtils.closeQuietly(manifestReader);
+                    Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
+                    try {
+                        return BundleManifestFactory.createBundleManifest(manifestReader);
+                    } finally {
+                        IOUtils.closeQuietly(manifestReader);
+                    }
+                } catch (IOException ioe) {
+                    throw new DeploymentException("Failed to read manifest for bundle from " + artifactFS, ioe);
                 }
-            } catch (IOException ioe) {
-                throw new DeploymentException("Failed to read manifest for bundle from " + artifactFS, ioe);
+            } else {
+                return BundleManifestFactory.createBundleManifest();
             }
-        } else {
-            return BundleManifestFactory.createBundleManifest();
+        } finally {
+            IOUtils.closeQuietly(manifestEntry);
         }
     }
 
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java
index 62e2441..65af9df 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/artifact/internal/bundle/StandardBundleInstallArtifact.java
@@ -66,7 +66,7 @@
 
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    private static final String MANIFEST_ENTRY_NAME = "/META-INF/MANIFEST.MF";
+    private static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF";
 
     private static final String EQUINOX_SYSTEM_BUNDLE_NAME = "org.eclipse.osgi";
 
@@ -141,15 +141,19 @@
 
     private BundleManifest getManifestFromArtifactFS() throws IOException {
         ArtifactFSEntry manifestEntry = this.artifactStorage.getArtifactFS().getEntry(MANIFEST_ENTRY_NAME);
-        if (manifestEntry != null && manifestEntry.exists()) {
-            Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
-            try {
-                return BundleManifestFactory.createBundleManifest(manifestReader);
-            } finally {
-                manifestReader.close();
+        try {
+            if (manifestEntry != null && manifestEntry.exists()) {
+                Reader manifestReader = new InputStreamReader(manifestEntry.getInputStream());
+                try {
+                    return BundleManifestFactory.createBundleManifest(manifestReader);
+                } finally {
+                    manifestReader.close();
+                }
+            } else {
+                return BundleManifestFactory.createBundleManifest();
             }
-        } else {
-            return BundleManifestFactory.createBundleManifest();
+        } finally {
+            IOUtils.closeQuietly(manifestEntry);
         }
     }
 
@@ -493,7 +497,9 @@
 
     public void deleteEntry(String targetPath) {
         deleteEntry(getQuasiBundle().getBundleFile(), targetPath);
-        getArtifactFS().getEntry(targetPath).delete();
+        ArtifactFSEntry entry = getArtifactFS().getEntry(targetPath);
+        entry.delete();
+        IOUtils.closeQuietly(entry);
     }
 
     private void deleteEntry(File root, String path) {
@@ -509,7 +515,12 @@
 
     public void updateEntry(URI inputPath, String targetPath) {
         updateEntry(getQuasiBundle().getBundleFile(), inputPath, targetPath);
-        updateEntry(getArtifactFS().getEntry(targetPath), inputPath);
+        ArtifactFSEntry entry = getArtifactFS().getEntry(targetPath);
+        try {
+            updateEntry(entry, inputPath);
+        } finally {
+            IOUtils.closeQuietly(entry);
+        }
     }
 
     private void updateEntry(ArtifactFSEntry entry, URI inputPath) {
diff --git a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformer.java b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformer.java
index cd5c738..1c302f2 100644
--- a/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformer.java
+++ b/org.eclipse.virgo.kernel.deployer/src/main/java/org/eclipse/virgo/kernel/install/pipeline/stage/transform/internal/SyntheticContextBundleCreatingTransformer.java
@@ -42,8 +42,8 @@
 import org.osgi.framework.Version;
 
 /**
- * A {@link Transformer} implementation that examines the install graph and, for each scoped plan found within the graph,
- * adds a synthetic context bundle to the plan.
+ * A {@link Transformer} implementation that examines the install graph and, for each scoped plan found within the
+ * graph, adds a synthetic context bundle to the plan.
  * 
  * <p />
  * 
@@ -83,21 +83,21 @@
             Set<BundleInstallArtifact> childBundles = getBundlesInScope(graph);
 
             PlanInstallArtifact planArtifact = (PlanInstallArtifact) graph.getValue();
-            
+
             String scopeName = determineSyntheticContextScopeName(planArtifact);
-            
+
             String name = scopeName + SYNTHETIC_CONTEXT_SUFFIX;
             Version version = planArtifact.getVersion();
-            
+
             ArtifactIdentity identity = new ArtifactIdentity(ArtifactIdentityDeterminer.BUNDLE_TYPE, name, version, scopeName);
-                        
+
             BundleManifest syntheticContextBundleManifest = createSyntheticContextBundleManifest(identity, childBundles);
-            
+
             ArtifactStorage artifactStorage = this.artifactStorageFactory.createDirectoryStorage(identity, name + ".jar");
             writeSyntheticContextBundle(syntheticContextBundleManifest, artifactStorage.getArtifactFS());
-            
-            GraphNode<InstallArtifact> syntheticContextBundle = this.installArtifactGraphFactory.constructInstallArtifactGraph(
-                identity, artifactStorage, null, null);
+
+            GraphNode<InstallArtifact> syntheticContextBundle = this.installArtifactGraphFactory.constructInstallArtifactGraph(identity,
+                artifactStorage, null, null);
             graph.addChild(syntheticContextBundle);
         }
     }
@@ -120,15 +120,19 @@
         return visitor.getChildBundles();
     }
 
-    private void writeSyntheticContextBundle(BundleManifest syntheticContextBundleManifest, ArtifactFS artifactFS) {                
+    private void writeSyntheticContextBundle(BundleManifest syntheticContextBundleManifest, ArtifactFS artifactFS) {
         ArtifactFSEntry entry = artifactFS.getEntry(JarFile.MANIFEST_NAME);
-        Writer manifestWriter = new OutputStreamWriter(entry.getOutputStream());
         try {
-            syntheticContextBundleManifest.write(manifestWriter);
-        } catch (IOException ioe) {
-            throw new FatalDeploymentException("Failed to write out synthetic context's manifest", ioe);
+            Writer manifestWriter = new OutputStreamWriter(entry.getOutputStream());
+            try {
+                syntheticContextBundleManifest.write(manifestWriter);
+            } catch (IOException ioe) {
+                throw new FatalDeploymentException("Failed to write out synthetic context's manifest", ioe);
+            } finally {
+                IOUtils.closeQuietly(manifestWriter);
+            }
         } finally {
-            IOUtils.closeQuietly(manifestWriter);
+            IOUtils.closeQuietly(entry);
         }
     }
 
@@ -148,7 +152,7 @@
     }
 
     private String determineSyntheticContextScopeName(PlanInstallArtifact plan) {
-        return ScopeNameFactory.createScopeName(plan.getName(), plan.getVersion());        
+        return ScopeNameFactory.createScopeName(plan.getName(), plan.getVersion());
     }
 
     private void addImportForEachChildBundle(BundleManifest bundleManifest, Set<BundleInstallArtifact> childBundles) {