[jboss-cvs] JBossAS SVN: r73901 - in projects/vfs/trunk/src: main/java/org/jboss/virtual/plugins/context and 6 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Jun 1 14:25:04 EDT 2008


Author: mstruk
Date: 2008-06-01 14:25:02 -0400 (Sun, 01 Jun 2008)
New Revision: 73901

Added:
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/SizeLimitedInputStream.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileLockReaper.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipWrapper.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/
   projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java
   projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryHandlerUnitTestCase.java
   projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryVFSContextUnitTestCase.java
Removed:
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java
Modified:
   projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVFSContext.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVirtualFileHandler.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/DelegatingHandler.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/file/FileSystemContext.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContext.java
   projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContextFactoryLocator.java
   projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/AbstractVFSContextTest.java
   projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java
   projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/UnpackTestCase.java
Log:
JBVFS-27 Merged-in jar-alter-work branch (72871:72996, 72997:73688, 73689:73900)

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -46,9 +46,11 @@
 import org.jboss.logging.Logger;
 import org.jboss.util.StringPropertyReplacer;
 import org.jboss.util.id.GUID;
+import org.jboss.virtual.plugins.context.DelegatingHandler;
 import org.jboss.virtual.plugins.context.file.FileSystemContext;
 import org.jboss.virtual.plugins.context.jar.AbstractJarHandler;
 import org.jboss.virtual.plugins.context.jar.NestedJarHandler;
+import org.jboss.virtual.plugins.context.zip.ZipEntryHandler;
 import org.jboss.virtual.spi.LinkInfo;
 import org.jboss.virtual.spi.VFSContext;
 import org.jboss.virtual.spi.VirtualFileHandler;
@@ -76,6 +78,17 @@
    public static final String FORCE_COPY_KEY = "jboss.vfs.forceCopy";
    public static final String USE_COPY_QUERY = "useCopyJarHandler";
 
+   /**
+    * Key used to force fallback from vfszip (default) to vfsjar
+    */
+   public static final String FORCE_VFS_JAR_KEY = "jboss.vfs.forceVfsJar";
+
+   /**
+    * Key used to turn off reaper mode in vfszip - forcing synchronous (slower) handling of files
+    */
+   public static final String FORCE_NO_REAPER_KEY = "jboss.vfs.forceNoReaper";
+   public static final String NO_REAPER_QUERY = "noReaper";
+
    private static File tempDir;
 
    private static class GetTempDir implements PrivilegedAction<File>
@@ -87,7 +100,7 @@
       }
    }
 
-   private synchronized static File getTempDirectory()
+   public synchronized static File getTempDirectory()
    {
       if (tempDir == null)
       {
@@ -494,7 +507,12 @@
 
       VirtualFileHandler handler = file.getHandler();
       // already unpacked
-      if (handler instanceof NestedJarHandler || handler instanceof AbstractJarHandler == false)
+      VirtualFileHandler unwrapped = handler;
+      if (unwrapped instanceof DelegatingHandler)
+         unwrapped = ((DelegatingHandler) unwrapped).getDelegate();
+
+      if (unwrapped instanceof ZipEntryHandler == false
+              && (unwrapped instanceof NestedJarHandler || unwrapped instanceof AbstractJarHandler == false))
       {
          if (log.isTraceEnabled())
             log.trace("Should already be unpacked: " + file);

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVFSContext.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVFSContext.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVFSContext.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -59,6 +59,9 @@
    /** Options associated with the root URL */
    private Map<String, String> rootOptions;
 
+   /** Root's peer within another context */
+   private VirtualFileHandler rootPeer;
+
    /**
     * Create a new AbstractVFSContext.
     * 
@@ -98,6 +101,16 @@
       return rootURI;
    }
 
+   public void setRootPeer(VirtualFileHandler handler)
+   {
+      this.rootPeer = handler;
+   }
+
+   public VirtualFileHandler getRootPeer()
+   {
+      return rootPeer;
+   }
+
    protected void addOption(String key, String value)
    {
       rootOptions.put(key, value);
@@ -124,6 +137,35 @@
       return parent.getChild(path);
    }
 
+   public URL getChildURL(VirtualFileHandler parent, String name) throws IOException
+   {
+      StringBuilder urlStr = new StringBuilder(256);
+      URI rootUri = getRootURI();
+      urlStr.append(rootUri.getScheme())
+              .append(":").append(rootUri.getPath());
+      if(parent != null)
+      {
+         String pPathName = null;
+         if(parent instanceof AbstractVirtualFileHandler)
+            pPathName = ((AbstractVirtualFileHandler)parent).getLocalPathName();
+         else
+            pPathName = parent.getPathName();
+
+         if (urlStr.charAt( urlStr.length()-1) != '/')
+            urlStr.append("/");
+
+         if(pPathName.length() != 0)
+            urlStr.append(pPathName);
+
+         if (urlStr.charAt( urlStr.length()-1) != '/')
+            urlStr.append("/");
+
+         urlStr.append(name);
+      }
+
+      return new URL(urlStr.toString());
+   }
+
    public void visit(VirtualFileHandler handler, VirtualFileHandlerVisitor visitor) throws IOException
    {
       if (handler == null)
@@ -204,7 +246,10 @@
          {
             try
             {
-               visit(child, visitor, false, leavesOnly, ignoreErrors, includeHidden, recurseFilter);
+               if (handler instanceof DelegatingHandler)
+                  child.getVFSContext().visit(child, visitor);
+               else
+                  visit(child, visitor, false, leavesOnly, ignoreErrors, includeHidden, recurseFilter);
             }
             catch (StackOverflowError e)
             {

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVirtualFileHandler.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVirtualFileHandler.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/AbstractVirtualFileHandler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -109,6 +109,7 @@
       this.context = context;
       this.parent = parent;
       this.name = VFSUtils.fixName(name);
+      this.vfsPath = null; // nullify possible invalid vfsPath initializations when running with debugger
    }
 
    /**
@@ -200,6 +201,29 @@
       this.vfsPath = path;
    }
 
+   public String getLocalPathName()
+   {
+      try
+      {
+         VirtualFileHandler handler = getVFSContext().getRoot();
+         String rootPathName = handler.getPathName();
+         String pathName = getPathName();
+         int len = rootPathName.length();
+         if (len == 0)
+            return pathName;
+         else if (rootPathName.length() < pathName.length())
+            return pathName.substring(rootPathName.length() + 1);
+         else
+            return "";
+      }
+      catch (IOException ex)
+      {
+         log.warn("Failed to compose local path name: context: " + getVFSContext() + ", name: " + getName(), ex);
+      }
+
+      return getPathName();
+   }
+
    public URL toURL() throws MalformedURLException, URISyntaxException
    {
       return toURI().toURL();
@@ -238,6 +262,10 @@
     */
    private boolean initPath(StringBuilder pathName)
    {
+      if (context.getRootPeer() != null)
+         if (initPeerPath(pathName))
+            return true;
+
       if (parent != null)
       {
          if (parent instanceof AbstractVirtualFileHandler)
@@ -255,7 +283,56 @@
       }
       return false;
    }
-   
+
+
+   private boolean initPeerPath(StringBuilder pathName)
+   {
+      VirtualFileHandler grandParent = null;
+
+      if (parent != null)
+      {
+         try
+         {
+            grandParent = parent.getParent();
+         }
+         catch(IOException ex)
+         {
+            // if we throw exception here we'll most likely cause an infinite recursion
+            log.warn("AbstractVirtualFileHandler.initPath failed: ctx: " + context
+                    + ", parent: " + parent + " name: " + name, ex);
+         }
+      }
+
+      VirtualFileHandler peer = context.getRootPeer();
+
+
+      if (grandParent == null)
+      {
+         // bypass parent and delegate straight to peer
+
+         if (peer instanceof AbstractVirtualFileHandler)
+         {
+            AbstractVirtualFileHandler handler = (AbstractVirtualFileHandler) peer;
+            if (handler.initPath(pathName) && parent != null)
+               pathName.append('/');
+         }
+         else
+         {
+            pathName.append(peer.getPathName());
+         }
+
+         if (parent != null)
+         {
+            // if it's a root node we skip adding '/' and a name
+            pathName.append(getName());
+         }
+
+         return true;
+      }
+
+      return false;
+   }
+
    public VirtualFile getVirtualFile()
    {
       checkClosed();

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/DelegatingHandler.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/DelegatingHandler.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/DelegatingHandler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -50,6 +50,18 @@
    private VirtualFileHandler delegate;
 
    /**
+    * Create a DelegatingHandler without a delegate - which will have to be set afterwards
+    *
+    * @param context - the context for the parent
+    * @param parent - the parent of the delegate in this VFS
+    * @param name - the name of the delegate in this VFS
+    */
+   public DelegatingHandler(VFSContext context, VirtualFileHandler parent, String name)
+   {
+      this(context, parent, name, null);
+   }
+
+   /**
     * Create a DelegatingHandler
     * 
     * @param context - the context for the parent
@@ -60,11 +72,21 @@
    public DelegatingHandler(VFSContext context, VirtualFileHandler parent, String name, VirtualFileHandler delegate)
    {
       super(context, parent, name);
-      if (delegate == null)
-         throw new IllegalArgumentException("Null delegate");
+      //if (delegate == null)
+      //   throw new IllegalArgumentException("Null delegate");
       this.delegate = delegate;
    }
 
+   public void setDelegate(VirtualFileHandler handler)
+   {
+      this.delegate = handler;
+   }
+
+   public VirtualFileHandler getDelegate()
+   {
+      return delegate;
+   }
+
    public VirtualFileHandler getChild(String path) throws IOException
    {
       return delegate.getChild(path);
@@ -119,4 +141,36 @@
    {
       delegate.replaceChild(original, replacement);
    }
+
+   public URL toVfsUrl() throws MalformedURLException, URISyntaxException
+   {
+      return delegate.toVfsUrl();
+   }
+
+   public int hashCode()
+   {
+      if (delegate != null)
+         return delegate.hashCode();
+
+      return super.hashCode();
+   }
+
+   public boolean equals(Object o)
+   {
+      if (o == this)
+         return true;
+      
+      if (o instanceof DelegatingHandler)
+      {
+         DelegatingHandler handler = (DelegatingHandler) o;
+         if (delegate != null)
+            return delegate.equals(handler.delegate);
+         else if (handler.delegate != null)
+            return false;   // one is null
+         else
+            return true;    // both are null
+      }
+
+      return false;
+   }
 }

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/file/FileSystemContext.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/file/FileSystemContext.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/file/FileSystemContext.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -27,26 +27,51 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.List;
 import java.util.Properties;
 
 import org.jboss.virtual.VFSUtils;
 import org.jboss.virtual.VirtualFile;
 import org.jboss.virtual.plugins.context.AbstractVFSContext;
+import org.jboss.virtual.plugins.context.DelegatingHandler;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContext;
 import org.jboss.virtual.plugins.context.jar.JarHandler;
 import org.jboss.virtual.plugins.context.jar.JarUtils;
 import org.jboss.virtual.spi.LinkInfo;
 import org.jboss.virtual.spi.VirtualFileHandler;
+import org.jboss.logging.Logger;
 
 /**
  * FileSystemContext.
+ *
+ * Jar archives are processed through {@link org.jboss.virtual.plugins.context.zip.ZipEntryContext}.
+ *
+ * To switch back to {@link org.jboss.virtual.plugins.context.jar.JarHandler}
+ * set a system property <em>jboss.vfs.forceVfsJar=true</em> 
  * 
  * @author <a href="adrian at jboss.com">Adrian Brock</a>
  * @author <a href="ales.justin at jboss.com">Ales Justin</a>
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
  * @version $Revision: 1.1 $
  */
 public class FileSystemContext extends AbstractVFSContext
 {
+
+   private static final Logger log = Logger.getLogger(ZipEntryContext.class);
+
+   /** true if forcing fallback to vfsjar from default vfszip */
+   private static boolean forceVfsJar;
+
+   static
+   {
+      forceVfsJar = AccessController.doPrivileged(new CheckForceVfsJar());
+
+      if (forceVfsJar)
+         log.warn("VFS forced fallback to vfsjar is enabled.");
+   }
+
    /** The root file */
    private final VirtualFileHandler root;
    
@@ -188,18 +213,45 @@
       String name = file.getName();
       if (file.isFile() && JarUtils.isArchive(name))
       {
-         try
+         if (forceVfsJar)
          {
             return new JarHandler(this, parent, file, file.toURL(), name);
          }
-         catch (IOException e)
+         else
          {
-            log.debug("Exception while trying to handle file (" + name + ") as a jar: " + e.getMessage());
+            try
+            {
+               DelegatingHandler delegator = mountZipFS(parent, name, file);
+               return delegator;
+            }
+            catch (Exception e)
+            {
+               IOException ex = new IOException("Exception while trying to handle file (" + name + ") through ZipEntryContext: ");
+               ex.initCause(e);
+               throw ex;
+            }
          }
       }
       return createVirtualFileHandler(parent, file, getFileURI(file));
    }
 
+   protected DelegatingHandler mountZipFS(VirtualFileHandler parent, String name, File file) throws IOException, URISyntaxException
+   {
+      DelegatingHandler delegator = new DelegatingHandler(this, parent, name);
+      URL fileUrl = file.toURL();
+      URL delegatorUrl = fileUrl;
+
+      if (parent != null)
+         delegatorUrl = getChildURL(parent, name);
+
+      ZipEntryContext ctx = new ZipEntryContext(delegatorUrl, delegator, fileUrl);
+
+      VirtualFileHandler handler = ctx.getRoot();
+      delegator.setDelegate(handler);
+
+      return delegator;
+   }
+   
    /**
     * Create a new virtual file handler
     * 
@@ -274,4 +326,14 @@
          rootFile.close();
       super.finalize();
    }
+
+   private static class CheckForceVfsJar implements PrivilegedAction<Boolean>
+   {
+      public Boolean run()
+      {
+         String forceString = System.getProperty(VFSUtils.FORCE_VFS_JAR_KEY, "false");
+         return Boolean.valueOf(forceString);
+      }
+   }
+
 }

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip)

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/SizeLimitedInputStream.java (from rev 73900, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/SizeLimitedInputStream.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/SizeLimitedInputStream.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/SizeLimitedInputStream.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,86 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * SizeLimitedInputStream
+ *
+ * Signals EOF when the specified number of bytes
+ * have been read from the underlying stream
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class SizeLimitedInputStream extends InputStream
+{
+
+	private InputStream in;
+
+   private int togo;
+
+	public SizeLimitedInputStream(InputStream ins, int size)
+   {
+		this.in = ins;
+		this.togo = size;
+	}
+
+	public int read() throws IOException
+   {
+		int b = -1;
+		if (togo > 0)
+      {
+			b = in.read();
+			if (b != -1)
+            togo--;
+		}
+		return b;
+	}
+
+	public int read(byte [] buf) throws IOException
+   {
+		return read(buf, 0, buf.length);
+	}
+
+	public int read(byte [] buf, int offs, int len) throws IOException
+   {
+		int rc = -1;
+
+		if (togo > 0)
+      {
+			rc = togo < len ? togo : len;
+			rc = in.read(buf, offs, rc);
+			if (rc != -1)
+            togo -= rc;
+		}
+
+		return rc;
+	}
+
+	public void close() throws IOException
+   {
+		in.close();
+	}
+}
\ No newline at end of file

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,639 +0,0 @@
-package org.jboss.virtual.plugins.context.zip;
-
-import org.jboss.virtual.plugins.context.AbstractVFSContext;
-import org.jboss.virtual.plugins.context.AbstractVirtualFileHandler;
-import org.jboss.virtual.plugins.context.DelegatingHandler;
-import org.jboss.virtual.plugins.context.jar.JarUtils;
-import org.jboss.virtual.spi.VirtualFileHandler;
-
-import java.io.*;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-/**
- * VFSContext exposing a zip archive file as a virtual file system
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class ZipEntryContext extends AbstractVFSContext
-{
-   static
-   {
-      deleteTmpDirContents();
-   }
-
-   /** File representing a zip archive that is virtualized with this context */
-   private File rootFile;
-
-   /** ZipFile opened around rootFile */
-   private ZipFileWrapper zipFile;
-
-   /** Entry path representing a context root */
-   private String rootEntryPath = "";
-
-   /** Auto clean signals if rootFile should be deleted after closing the context */
-   private boolean autoClean = false;
-
-   /** Entries in a hierarchy */
-   private ConcurrentHashMap<String, EntryInfo> entries = new ConcurrentHashMap<String, EntryInfo>();
-
-   /**
-    * Create a new ZipEntryContext
-    *
-    * @param rootURL - file or jar:file url
-    * @throws URISyntaxException
-    * @throws IOException
-    */
-   public ZipEntryContext(URL rootURL) throws URISyntaxException, IOException
-   {
-      this(rootURL, false);
-   }
-
-   /**
-    * Create a new ZipEntryContext
-    *
-    * @param rootURL - file or jar:file url
-    * @param autoClean - true if file represented by rootURL should be deleted after this context is closed
-    * @throws URISyntaxException
-    * @throws IOException
-    */
-   public ZipEntryContext(URL rootURL, boolean autoClean) throws URISyntaxException, IOException
-   {
-      super(fixUrl(rootURL));
-      this.autoClean = autoClean;
-      init(rootURL, null);
-   }
-
-   /**
-    * Create a new ZipEntryContext being mounted into another context
-    *
-    * @param rootURL - url representing this context within another context
-    * @param peer - file handler in another context through which this context is being mounted
-    * @param localRootUrl - file or jar:file url
-    * @throws URISyntaxException
-    * @throws IOException
-    */
-   public ZipEntryContext(URL rootURL, VirtualFileHandler peer, URL localRootUrl) throws URISyntaxException, IOException
-   {
-      this(rootURL, peer, localRootUrl, false);
-   }
-
-   /**
-    * Create a new ZipEntryContext being mounted into another context
-    *
-    * @param rootURL - url representing this context within another context
-    * @param peer - file handler in another context through which this context is being mounted
-    * @param localRootUrl - file or jar:file url
-    * @param autoClean - true if file represented by localRootURL should be deleted after this context is closed
-    * @throws URISyntaxException
-    * @throws IOException
-    */
-   public ZipEntryContext(URL rootURL, VirtualFileHandler peer, URL localRootUrl, boolean autoClean) throws URISyntaxException, IOException
-   {
-      super(fixUrl(rootURL));
-      this.autoClean = autoClean;
-      init(localRootUrl, peer);
-   }
-
-   private void init(URL localRootURL, VirtualFileHandler peer) throws IOException, URISyntaxException
-   {
-      initFileAndPath(localRootURL);
-
-      if(!rootFile.isFile())
-         throw new RuntimeException("File not found: " + rootFile);
-
-      //try
-      //{
-         zipFile = new ZipFileWrapper(rootFile);
-      //}
-      //catch(Exception ex)
-      //{
-      //   throw new RuntimeException("Failed to open the file as zip: " + rootFile, ex);
-      //}
-
-      setRootPeer(peer);
-
-      String name = getRootURI().toString();   // rootFile.getName()
-      int toPos = name.length();
-      if(name.length() != 0 && name.charAt(name.length()-1) == '/')
-         toPos --;
-
-      int namePos = name.lastIndexOf("/", toPos-1);
-      name = name.substring(namePos+1, toPos);
-
-      if(name.length() != 0 && name.charAt(name.length()-1) == '!')
-         name = name.substring(0, name.length()-1);
-
-      // init initial root that will be overwritten if it exists as ZipEntry
-      entries.put("", new EntryInfo(new ZipEntryHandler(this, null, name), null));
-
-      initEntries();
-
-      ZipEntryContextFactory.registerContext(this);
-   }
-
-   public String getName()
-   {
-      VirtualFileHandler peer = getRootPeer();
-      if (peer != null)
-         return peer.getName();
-      else
-         return rootFile.getName();
-   }
-
-
-/*
-   public URL getChildURL(AbstractVirtualFileHandler parent, String name) {
-      try
-      {
- // --
-         StringBuilder url = new StringBuilder();
-         VirtualFileHandler peer = getRootPeer();
-         if (peer != null)
-         {
-            VFSContext peerCtx = peer.getVFSContext();
-            if (peerCtx instanceof AbstractVFSContext)
-
-               url.append( ((AbstractVirtualFileHandler) peer).getGlobalChildURI() );
-         }
-         String parentPath = parent.getLocalPathName();
-         url.append(parentPath);
-         if(!"".equals(parentPath))
-            url.append("/");
-         url.append(name);
- // --
-
-         StringBuilder urlStr = new StringBuilder(250);
-         urlStr.append(getRootURI().toString());
-         if(parent != null)
-         {
-            String pPathName = parent.getLocalPathName();
-            if(pPathName.length() != 0)
-               urlStr.append("/").append(pPathName);
-
-            urlStr.append("/").append(name);
-         }
-
-         return new URL(urlStr.toString());
-      }
-      catch(Exception ex)
-      {
-         throw new IllegalArgumentException("Name could not be converted to URL: " + name, ex);
-      }
-   }
-*/
-
-   private synchronized void initEntries() throws IOException, URISyntaxException
-   {
-
-      // we're using a two phase approach - we first select the relevant ones
-      // then we order these by name and only then we process them
-      // this way we ensure that parent entries are processed before child entries
-
-      HashMap<String, ZipEntry> relevant = new HashMap<String, ZipEntry>();
-      ZipFile zFile = zipFile.acquire();
-      try
-      {
-         Enumeration<? extends ZipEntry> zipEntries = zFile.entries();
-         while(zipEntries.hasMoreElements())
-         {
-            ZipEntry ent = zipEntries.nextElement();
-            if(ent.getName().startsWith(rootEntryPath))
-            {
-               relevant.put(ent.getName(), ent);
-            }
-         }
-
-         TreeMap<String, ZipEntry> orderedRelevant = new TreeMap<String, ZipEntry>(relevant);
-
-         for(Map.Entry<String, ZipEntry> entry : orderedRelevant.entrySet())
-         {
-            ZipEntry ent = entry.getValue();
-            String fullName = ent.getName().substring(rootEntryPath.length());
-
-            String [] split = splitParentChild(fullName);
-            String parentPath = split[0];
-            String name = split[1];
-
-            EntryInfo ei = entries.get(parentPath);
-            if(ei == null)
-               ei = makeDummyParent(parentPath);
-
-            AbstractVirtualFileHandler parent = ei != null ? ei.handler : null;
-
-            if(!ent.isDirectory() && JarUtils.isArchive(ent.getName()))
-            {
-               // extract it to temp dir
-               File dest = new File(getTempDir() + "/" + getTempFileName(ent.getName()));
-               dest.getParentFile().mkdirs();  // ensure parent exists
-               InputStream is = zipFile.openStream(ent);
-               OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
-               copyStreamAndClose(is, os);
-
-               // mount FS
-               DelegatingHandler delegator = mountZipFS(parent, name, dest);
-
-               entries.put(delegator.getLocalPathName(), new EntryInfo(delegator, ent));
-               addChild(parent, delegator);
-            }
-            else
-            {
-               ZipEntryHandler wrapper = new ZipEntryHandler(this, parent, name);
-               entries.put(wrapper.getLocalPathName(), new EntryInfo(wrapper, ent));
-            }
-
-         }
-      }
-      finally
-      {
-         zipFile.release();
-      }
-
-   }
-
-   protected DelegatingHandler mountZipFS(VirtualFileHandler parent, String name, File file) throws IOException, URISyntaxException
-   {
-      DelegatingHandler delegator = new DelegatingHandler(this, parent, name);
-      URL fileUrl = file.toURL();
-      URL delegatorUrl = fileUrl;
-
-      if (parent != null)
-         delegatorUrl = getChildURL(parent, name);
-         //delegatorUrl = new URL(VFSUtils.getChildURLString(parent.toURL(), name));
-
-
-      ZipEntryContext ctx = new ZipEntryContext(delegatorUrl, delegator, fileUrl, true);
-
-      VirtualFileHandler handler = ctx.getRoot();
-      delegator.setDelegate(handler);
-
-      return delegator;
-   }
-
-   private EntryInfo makeDummyParent(String parentPath) throws IOException
-   {
-      // get grand parent first
-      String [] split = splitParentChild(parentPath);
-      String grandPa = split[0];
-
-      EntryInfo eiParent = entries.get(grandPa);
-      if(eiParent == null)
-         eiParent = makeDummyParent(grandPa);
-
-      ZipEntryHandler handler = new ZipEntryHandler(this, eiParent.handler, split[1]);
-      EntryInfo ei = new EntryInfo(handler, null);
-      entries.put(parentPath, ei);
-      return ei;
-   }
-
-   private void initFileAndPath(URL localRootUrl)
-   {
-      String filePath = localRootUrl.toString();
-      String zipPath = filePath;
-
-      int pos = filePath.indexOf("!");
-      if(pos > 0)
-      {
-         zipPath = filePath.substring(0, pos);
-         rootEntryPath = filePath.substring(pos+2);
-         if(rootEntryPath.length() != 0)
-            rootEntryPath += "/";
-      }
-
-      try
-      {
-         // find where schema ends - all schemas when you have wrapping ...
-         pos= zipPath.indexOf(":/");
-         filePath = "file:" + zipPath.substring(pos+1);
-         rootFile = new File(new URI(filePath));
-         if (autoClean)
-            rootFile.deleteOnExit();
-      }
-      catch(Exception ex)
-      {
-         throw new RuntimeException("ASSERTION ERROR - Could not create URI: " + filePath, ex);
-      }
-   }
-
-   public VirtualFileHandler getRoot() throws IOException
-   {
-      return entries.get("").handler;
-   }
-
-   private synchronized void checkIfModified()
-   {
-      // our rootFile may be a derivative - the extracted insides of another archive
-      // if RootContextInfo is available - delegate to it
-      // otherwise work on the file directly
-      if (zipFile.hasBeenModified())
-      {
-         EntryInfo rootInfo = entries.get("");
-         entries = new ConcurrentHashMap<String, EntryInfo>();
-         entries.put("", rootInfo);
-
-         if (zipFile.exists())
-         {
-            try
-            {
-               initEntries();
-            }
-            catch(Exception ex)
-            {
-               log.warn("Failed to reinitialize context: " + getRootURI());
-            }
-         }
-      }
-
-   }
-
-   public VirtualFileHandler getChild(ZipEntryHandler parent, String name)
-   {
-      checkIfModified();
-      String pathName = parent.getLocalPathName();
-      if("".equals(pathName))
-         pathName = name;
-      else
-         pathName = pathName + "/" + name;
-
-      EntryInfo ei = entries.get(pathName);
-      
-      if(ei != null)
-         return ei.handler;
-
-      return null;
-   }
-
-
-   public List<VirtualFileHandler> getChildren(VirtualFileHandler parent, boolean ignoreErrors) throws IOException
-   {
-      checkIfModified();
-      if(parent instanceof AbstractVirtualFileHandler)
-      {
-         AbstractVirtualFileHandler parentHandler  = (AbstractVirtualFileHandler) parent;
-         EntryInfo parentEntry = entries.get(parentHandler.getLocalPathName());
-         if (parentEntry != null)
-         {
-            if (parentEntry.handler instanceof DelegatingHandler)
-               return parentEntry.handler.getChildren(ignoreErrors);
-            
-            List<AbstractVirtualFileHandler> children = parentEntry.getChildren();
-            return Collections.unmodifiableList(new LinkedList<VirtualFileHandler>(children));
-         }
-      }
-      return Collections.emptyList();
-   }
-
-
-   public long getLastModified(ZipEntryHandler handler)
-   {
-      checkIfModified();
-      EntryInfo ei = entries.get(handler.getLocalPathName());
-      if(ei == null)
-         return 0;
-
-      if(ei.entry == null) {
-         return rootFile.lastModified();
-      }
-
-      return ei.entry.getTime();
-   }
-
-   public long getSize(ZipEntryHandler handler)
-   {
-      checkIfModified();
-      String pathName = handler.getLocalPathName();
-      EntryInfo ei = entries.get(pathName);
-      if(ei == null)
-         return 0;
-
-      if(ei.entry == null)
-      {
-         if(pathName.length() == 0)
-            return rootFile.length();
-         else
-            return 0;
-      }
-
-      return ei.entry.getSize();
-   }
-
-   public boolean exists(ZipEntryHandler handler)
-   {
-      checkIfModified();
-      EntryInfo ei = entries.get(handler.getLocalPathName());
-      if(ei == null)
-         return false;
-
-      return true;
-   }
-
-   public boolean isLeaf(ZipEntryHandler handler)
-   {
-      checkIfModified();
-      EntryInfo ei = entries.get(handler.getLocalPathName());
-      if(ei == null || ei.entry == null)
-         return false;
-
-      return !ei.entry.isDirectory();
-   }
-
-   public InputStream openStream(ZipEntryHandler handler) throws IOException
-   {
-      checkIfModified();
-      EntryInfo ei = entries.get(handler.getLocalPathName());
-
-      if (ei == null)
-      {
-         String uriStr = "";
-         try
-         {
-            uriStr = handler.toURI().toString();
-         }
-         catch(Exception ex)
-         {
-            throw new RuntimeException("ASSERTION ERROR - uri generation failed for ZipEntryHandler: " + handler);
-         }
-         throw new FileNotFoundException(uriStr);
-
-      }
-
-      if(ei.entry == null)
-      {
-         return new FileInputStream(rootFile); 
-      }
-      
-      return zipFile.openStream(ei.entry);
-   }
-
-   public void addChild(AbstractVirtualFileHandler parent, AbstractVirtualFileHandler child)
-   {
-      EntryInfo parentEntry = entries.get(parent.getLocalPathName());
-      if (parentEntry != null)
-         parentEntry.getChildren().add(child);
-      else throw new RuntimeException("Parent does not exist: " + parent);
-   }
-
-
-
-   protected void finalize()
-   {
-      try
-      {
-         zipFile.close();
-         if (autoClean)
-            rootFile.delete();
-      }
-      catch (Exception ex)
-      {
-         // ignore
-      }
-   }
-
-
-   /**
-    *  Internal data structure, holding ZipEntries and child handlers for every handler in this context
-    */
-   static class EntryInfo
-   {
-      AbstractVirtualFileHandler handler;
-      ZipEntry entry;
-      List<AbstractVirtualFileHandler> children;
-
-      EntryInfo(AbstractVirtualFileHandler handler, ZipEntry entry)
-      {
-         this.handler = handler;
-         this.entry = entry;
-      }
-
-      public synchronized List<AbstractVirtualFileHandler> getChildren()
-      {
-         if (children == null)
-            children = new LinkedList<AbstractVirtualFileHandler>();
-
-         return children;
-      }
-   }
-
-
-   //
-   //   Helper methods
-   //
-
-   private static URL fixUrl(URL rootURL) throws MalformedURLException
-   {
-      if (!"vfszip".equals(rootURL.getProtocol()))
-      {
-         String url = rootURL.toString();
-         int pos = url.indexOf(":/");
-         if (pos != -1)
-            url = url.substring(pos);
-
-         return new URL("vfszip" + url);
-      }
-      return rootURL;
-   }
-
-   public static String [] splitParentChild(String pathName)
-   {
-      if(pathName.length() == 0)
-         return new String [] {null, pathName};
-
-      int toPos = pathName.length();
-      if(pathName.charAt(pathName.length()-1) == '/')
-         toPos --;
-
-      int delimPos = pathName.lastIndexOf('/', toPos-1);
-
-      String [] ret;
-      if(delimPos == -1)
-      {
-         ret = new String []
-         {
-            "",
-            pathName.substring(delimPos+1, toPos)
-         };
-      }
-      else
-      {
-         ret = new String []
-         {
-            pathName.substring(0, delimPos),
-            pathName.substring(delimPos+1, toPos)
-         };
-      }
-      return ret;
-   }
-
-   private static void copyStreamAndClose(InputStream is, OutputStream os) throws IOException
-   {
-      try
-      {
-         byte [] buff = new byte[65536];
-         int count = is.read(buff);
-         while(count != -1)
-         {
-            os.write(buff, 0, count);
-            count = is.read(buff);
-         }
-      }
-      finally
-      {
-         if(is != null)
-         {
-            try {
-               is.close();
-            }
-            catch(Exception ex)
-            {
-               // ignore
-            }
-         }
-         os.close();
-      }
-   }
-
-
-   // TODO: the following two methods are a quick and dirty strategy for temp file name and path
-   private static String getTempFileName(String name)
-   {
-      int delim = name.lastIndexOf("/");
-      if (delim != -1)
-         name = name.substring(delim+1);
-      return UUID.randomUUID().toString() + "_" + name;
-   }
-
-   private static String getTempDir()
-   {
-      File dir = new File(System.getProperty("jboss.server.temp.dir"), "vfs");
-      //dir.mkdirs();
-      return dir.toString();
-   }
-
-   private static void deleteTmpDirContents()
-   {
-      try
-      {
-         File tmpDir = new File(getTempDir());
-         File [] files = tmpDir.listFiles();
-         for (File file: files)
-         {
-            if (!file.isDirectory() && !file.isHidden())
-               file.delete();
-         }
-      }
-      catch(Exception ex)
-      {
-        // ignore
-      }
-
-   }
-
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,929 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import org.jboss.logging.Logger;
+import org.jboss.virtual.VFSUtils;
+import org.jboss.virtual.plugins.context.AbstractVFSContext;
+import org.jboss.virtual.plugins.context.AbstractVirtualFileHandler;
+import org.jboss.virtual.plugins.context.DelegatingHandler;
+import org.jboss.virtual.plugins.context.jar.JarUtils;
+import org.jboss.virtual.spi.VirtualFileHandler;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.ZipEntry;
+
+/**
+ * <tt>ZipEntryContext</tt> implements a {@link org.jboss.virtual.spi.VFSContext}
+ * that exposes a zip archive as a virtual file system.
+ *
+ * Zip archive can be in a form of a file or a stream.
+ *
+ * Nested archives are processed through this same class.
+ * By default nested archives are cached in memory and mounted as new
+ * instances of <tt>ZipEntryContext</tt> with <tt>ZipStremWrapper</tt> as a source.
+ * If system property <em>jboss.vfs.forceCopy=true</em> is specified,
+ * or URL query parameter <em>forceCopy=true</em> is present,
+ * nested archives are extracted into a temp directory before being
+ * mounted as new instances of <tt>ZipEntryContext</tt>.
+ *
+ * This context implementation has two modes of releasing file locks.
+ * <em>Asynchronous</em> mode is the default one since it is better performant.
+ * To switch this to <em>synchronous</em> mode a system property
+ * <em>jboss.vfs.forceNoReaper=true</em> can be specified or URL query parameter
+ * <em>noReaper=true</em> can be included in context URL.
+ *
+ * This context implementation is a replacement for
+ * {@link org.jboss.virtual.plugins.context.jar.JarContext}.
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class ZipEntryContext extends AbstractVFSContext
+{
+
+   private static final Logger log = Logger.getLogger(ZipEntryContext.class);
+
+   /** Global setting for nested archive processing mode: copy or no-copy (default) */
+   private static boolean forceCopy;
+
+   static
+   {
+      deleteTmpDirContents();
+
+      forceCopy = AccessController.doPrivileged(new CheckForceCopy());
+
+      if (forceCopy)
+         log.info("VFS force nested jars copy-mode is enabled.");
+   }
+
+
+   /** Abstracted access to zip archive - either ZipFileWrapper or ZipStreamWrapper */
+   private ZipWrapper zipSource;
+
+   /** Entry path representing a context root - archive root is not necessarily a context root */
+   private String rootEntryPath = "";
+
+   /** AutoClean signals if zip archive should be deleted after closing the context - true for nested archives */
+   private boolean autoClean = false;
+
+   /** Registry of everything that zipSource contains */
+   private ConcurrentHashMap<String, EntryInfo> entries = new ConcurrentHashMap<String, EntryInfo>();
+
+
+   /**
+    * Create a new ZipEntryContext
+    *
+    * @param rootURL - file or jar:file url
+    * @throws URISyntaxException
+    * @throws IOException
+    */
+   public ZipEntryContext(URL rootURL) throws URISyntaxException, IOException
+   {
+      this(rootURL, false);
+   }
+
+   /**
+    * Create a new ZipEntryContext
+    *
+    * @param rootURL - file or jar:file url
+    * @param autoClean - true if file represented by rootURL should be deleted after this context is closed
+    * @throws URISyntaxException
+    * @throws java.io.IOException
+    */
+   public ZipEntryContext(URL rootURL, boolean autoClean) throws URISyntaxException, IOException
+   {
+      super(VFSUtils.toURI(fixUrl(rootURL)));
+      this.autoClean = autoClean;
+      init(rootURL, null, null);
+   }
+
+   /**
+    * Create a new ZipEntryContext to be mounted into another context
+    *
+    * @param rootURL - url representing this context within another context
+    * @param peer - file handler in another context through which this context is being mounted
+    * @param localRootUrl - file or jar:file url
+    * @throws URISyntaxException
+    * @throws java.io.IOException
+    */
+   public ZipEntryContext(URL rootURL, VirtualFileHandler peer, URL localRootUrl) throws URISyntaxException, IOException
+   {
+      this(rootURL, peer, localRootUrl, false);
+   }
+
+   /**
+    * Create a new ZipEntryContext to be mounted into another context
+    *
+    * @param rootURL - url representing this context within another context
+    * @param peer - file handler in another context through which this context is being mounted
+    * @param localRootUrl - file or jar:file url
+    * @param autoClean - true if file represented by localRootURL should be deleted after this context is closed
+    * @throws URISyntaxException
+    * @throws IOException
+    */
+   public ZipEntryContext(URL rootURL, VirtualFileHandler peer, URL localRootUrl, boolean autoClean) throws URISyntaxException, IOException
+   {
+      super(VFSUtils.toURI(fixUrl(rootURL)));
+      this.autoClean = autoClean;
+      init(localRootUrl, peer, null);
+   }
+
+   /**
+    * Create a new ZipEntryContext to be mounted into another context
+    *
+    * @param rootURL - url representing this context within another context
+    * @param peer - file handler in another context through which this context is being mounted
+    * @param zipWrapper - abstracted zip archive source
+    * @param autoClean - true if file represented by localRootURL should be deleted after this context is closed
+    * @throws URISyntaxException
+    * @throws IOException
+    */
+   public ZipEntryContext(URL rootURL, VirtualFileHandler peer, ZipWrapper zipWrapper, boolean autoClean) throws URISyntaxException, IOException
+   {
+      super(VFSUtils.toURI(fixUrl(rootURL)));
+      this.autoClean = autoClean;
+      init(null, peer, zipWrapper);
+   }
+
+   /**
+    * Extra initialization that couldn't fit inside constructors
+    *
+    * @param localRootURL
+    * @param peer
+    * @param zipWrapper
+    * @throws IOException
+    * @throws URISyntaxException
+    */
+   private void init(URL localRootURL, VirtualFileHandler peer, ZipWrapper zipWrapper) throws IOException, URISyntaxException
+   {
+
+      if (zipWrapper == null)
+      {
+         if (localRootURL == null)
+            throw new IllegalArgumentException("No ZipWrapper specified and localRootURL is null");
+
+         // initialize rootEntryPath and get archive file path
+         String rootPath = initRootAndPath(localRootURL);
+
+         String noReaper = getOptions().get(VFSUtils.NO_REAPER_QUERY);
+         zipSource = new ZipFileWrapper(VFSUtils.toURI(new URL(rootPath)), autoClean, Boolean.valueOf(noReaper));
+      }
+      else
+      {
+         zipSource = zipWrapper;
+      }
+      
+      setRootPeer(peer);
+
+      String name = getRootURI().toString();
+      int toPos = name.length();
+
+      // cut off any ending slash
+      if(name.length() != 0 && name.charAt(name.length()-1) == '/')
+         toPos --;
+
+      // name is last path component
+      int namePos = name.lastIndexOf("/", toPos-1);
+      name = name.substring(namePos+1, toPos);
+      
+      // cut off any ending exclamation
+      if(name.length() != 0 && name.charAt(name.length()-1) == '!')
+         name = name.substring(0, name.length()-1);
+
+      // init initial root EntryInfo that will be overwritten
+      // if zip entry exists for rootEntryPath
+      entries.put("", new EntryInfo(new ZipEntryHandler(this, null, name, true), null));
+
+      initEntries();
+      ZipEntryContextFactory.registerContext(this);
+   }
+
+   /**
+    * Returns archive file name - if this is a top-level ZipEntryContext.
+    * Otherwise it returns the last component of URL.
+    *
+    * @return name
+    */
+   public String getName()
+   {
+      VirtualFileHandler peer = getRootPeer();
+      if (peer != null)
+         return peer.getName();
+      else
+         return zipSource.getName();
+   }
+
+   /**
+    * Iterate through zip archive entries, compose a tree structure of archive's content
+    *
+    * @throws IOException
+    * @throws URISyntaxException
+    */
+   private synchronized void initEntries() throws IOException, URISyntaxException
+   {
+
+      // we're using a two phase approach - we first select the relevant ones
+      // then we order these by name and only then we process them
+      // this way we ensure that parent entries are processed before child entries
+
+      HashMap<String, ZipEntry> relevant = new HashMap<String, ZipEntry>();
+      zipSource.acquire();
+      try
+      {
+         Enumeration<? extends ZipEntry> zipEntries = zipSource.entries();
+
+         // zoom-in on entries under rootEntryPath - ignoring the rest
+         while(zipEntries.hasMoreElements())
+         {
+            ZipEntry ent = zipEntries.nextElement();
+            if(ent.getName().startsWith(rootEntryPath))
+            {
+               relevant.put(ent.getName(), ent);
+            }
+         }
+
+         TreeMap<String, ZipEntry> orderedRelevant = new TreeMap<String, ZipEntry>(relevant);
+
+         for(Map.Entry<String, ZipEntry> entry : orderedRelevant.entrySet())
+         {
+            ZipEntry ent = entry.getValue();
+            String fullName = ent.getName().substring(rootEntryPath.length());
+
+            String [] split = splitParentChild(fullName);
+            String parentPath = split[0];
+            String name = split[1];
+
+            EntryInfo ei = entries.get(parentPath);
+            if(ei == null)
+               ei = makeDummyParent(parentPath);
+
+            AbstractVirtualFileHandler parent = ei != null ? ei.handler : null;
+
+            if(ent.isDirectory() == false && JarUtils.isArchive(ent.getName()))
+            {
+               boolean useCopyMode = forceCopy;
+               if (useCopyMode == false)
+               {
+                  String flag = getOptions().get(VFSUtils.USE_COPY_QUERY);
+                  useCopyMode = Boolean.valueOf(flag);
+               }
+
+               DelegatingHandler delegator;
+
+               if (useCopyMode)
+               {
+                  // extract it to temp dir
+                  File dest = new File(getTempDir() + "/" + getTempFileName(ent.getName()));
+                  dest.deleteOnExit();
+
+                  // ensure parent exists
+                  dest.getParentFile().mkdirs();
+
+                  InputStream is = zipSource.openStream(ent);
+                  OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
+                  copyStreamAndClose(is, os);
+
+                  // mount another instance of ZipEntryContext
+                  delegator = mountZipFile(parent, name, dest);
+               }
+               else
+               {
+                  // mount another instance of ZipEntryContext
+                  delegator = mountZipStream(parent, name, zipSource.openStream(ent));
+               }
+
+               entries.put(delegator.getLocalPathName(), new EntryInfo(delegator, ent));
+               addChild(parent, delegator);
+            }
+            else
+            {
+               ZipEntryHandler wrapper = new ZipEntryHandler(this, parent, name, ent.isDirectory() == false);
+               entries.put(wrapper.getLocalPathName(), new EntryInfo(wrapper, ent));
+            }
+
+         }
+      }
+      finally
+      {
+         zipSource.release();
+      }
+
+   }
+
+   /**
+    * Mount ZipEntryContext created around extracted nested archive
+    *
+    * @param parent
+    * @param name
+    * @param file
+    * @return
+    * @throws IOException
+    * @throws URISyntaxException
+    */
+   protected DelegatingHandler mountZipFile(VirtualFileHandler parent, String name, File file) throws IOException, URISyntaxException
+   {
+      DelegatingHandler delegator = new DelegatingHandler(this, parent, name);
+      URL fileUrl = file.toURL();
+      URL delegatorUrl = fileUrl;
+
+      if (parent != null)
+         delegatorUrl = getChildURL(parent, name);
+
+      ZipEntryContext ctx = new ZipEntryContext(delegatorUrl, delegator, fileUrl, true);
+      VirtualFileHandler handler = ctx.getRoot();
+      delegator.setDelegate(handler);
+
+      return delegator;
+   }
+
+   /**
+    * Mount ZipEntryContext created around ZipStreamWrapper
+    *
+    * @param parent
+    * @param name
+    * @param zipStream
+    * @return
+    * @throws IOException
+    * @throws URISyntaxException
+    */
+   protected DelegatingHandler mountZipStream(VirtualFileHandler parent, String name, InputStream zipStream) throws IOException, URISyntaxException
+   {
+      DelegatingHandler delegator = new DelegatingHandler(this, parent, name);
+      ZipStreamWrapper wrapper = new ZipStreamWrapper(zipStream, name, parent.getLastModified());
+
+      URL delegatorUrl = null;
+
+      if (parent != null)
+         delegatorUrl = getChildURL(parent, name);
+
+      ZipEntryContext ctx = new ZipEntryContext(delegatorUrl, delegator, wrapper, false);
+      VirtualFileHandler handler = ctx.getRoot();
+      delegator.setDelegate(handler);
+
+      return delegator;
+   }
+
+   /**
+    * Zip archives sometimes don't contain directory entries - only leaf entries
+    *
+    * @param parentPath
+    * @return
+    * @throws IOException
+    */
+   private EntryInfo makeDummyParent(String parentPath) throws IOException
+   {
+      // get grand parent first
+      String [] split = splitParentChild(parentPath);
+      String grandPa = split[0];
+
+      EntryInfo eiParent = entries.get(grandPa);
+      if(eiParent == null)
+         eiParent = makeDummyParent(grandPa);
+
+      ZipEntryHandler handler = new ZipEntryHandler(this, eiParent.handler, split[1], false);
+      EntryInfo ei = new EntryInfo(handler, null);
+      entries.put(parentPath, ei);
+      return ei;
+   }
+
+   /**
+    * Initialize rootEntryPath and return archive file path
+    *
+    * @param localRootUrl
+    * @return
+    */
+   private String initRootAndPath(URL localRootUrl)
+   {
+      String filePath = localRootUrl.toString();
+      String zipPath = filePath;
+
+      int pos = filePath.indexOf("!");
+      if(pos > 0)
+      {
+         zipPath = filePath.substring(0, pos);
+         rootEntryPath = filePath.substring(pos+2);
+         if(rootEntryPath.length() != 0)
+            rootEntryPath += "/";
+      }
+
+      // find where url protocol ends - i.e. jar:file:/ ...
+      pos= zipPath.indexOf(":/");
+      filePath = zipPath.substring(pos+1);
+
+      // cut out url query part if present
+      int queryStart = filePath.indexOf("?");
+      if (queryStart != -1)
+         filePath = filePath.substring(0, queryStart);
+       
+      return "file:" + filePath;
+   }
+
+   /**
+    * If archive has been modified, clear <em>entries</em> and re-initialize
+    */
+   private synchronized void checkIfModified()
+   {
+      // TODO: if zipSource represents a nested archive we should maybe delegate lastModified to its parent
+      if (zipSource.hasBeenModified())
+      {
+         EntryInfo rootInfo = entries.get("");
+         entries = new ConcurrentHashMap<String, EntryInfo>();
+         entries.put("", rootInfo);
+
+         if (zipSource.exists())
+         {
+            try
+            {
+               initEntries();
+            }
+            catch(Exception ignored)
+            {
+               log.warn("IGNORING: Failed to reinitialize context: " + getRootURI(), ignored);
+            }
+         }
+      }
+   }
+
+   public VirtualFileHandler getRoot() throws IOException
+   {
+      return entries.get("").handler;
+   }
+
+   public VirtualFileHandler getChild(ZipEntryHandler parent, String name)
+   {
+      if (parent == null)
+         throw new IllegalArgumentException("Null parent");
+
+      checkIfModified();
+      String pathName = parent.getLocalPathName();
+      if("".equals(pathName))
+         pathName = name;
+      else
+         pathName = pathName + "/" + name;
+
+      EntryInfo ei = entries.get(pathName);
+      
+      if(ei != null)
+         return ei.handler;
+
+      return null;
+   }
+
+   public List<VirtualFileHandler> getChildren(VirtualFileHandler parent, boolean ignoreErrors) throws IOException
+   {
+      if (parent == null)
+         throw new IllegalArgumentException("Null parent");
+
+      checkIfModified();
+      if(parent instanceof AbstractVirtualFileHandler)
+      {
+         AbstractVirtualFileHandler parentHandler  = (AbstractVirtualFileHandler) parent;
+         EntryInfo parentEntry = entries.get(parentHandler.getLocalPathName());
+         if (parentEntry != null)
+         {
+            if (parentEntry.handler instanceof DelegatingHandler)
+               return parentEntry.handler.getChildren(ignoreErrors);
+            
+            return parentEntry.getChildren();
+         }
+      }
+      return Collections.emptyList();
+   }
+
+   public long getLastModified(ZipEntryHandler handler)
+   {
+      if (handler == null)
+         throw new IllegalArgumentException("Null handler");
+
+      checkIfModified();
+      EntryInfo ei = entries.get(handler.getLocalPathName());
+      if(ei == null)
+         return 0;
+
+      if(ei.entry == null) {
+         return zipSource.getLastModified();
+      }
+
+      return ei.entry.getTime();
+   }
+
+   public long getSize(ZipEntryHandler handler)
+   {
+      if (handler == null)
+         throw new IllegalArgumentException("Null handler");
+
+      checkIfModified();
+      String pathName = handler.getLocalPathName();
+      EntryInfo ei = entries.get(pathName);
+      if(ei == null)
+         return 0;
+
+      if(ei.entry == null)
+      {
+         if(pathName.length() == 0)
+            return zipSource.getSize();
+         else
+            return 0;
+      }
+
+      return ei.entry.getSize();
+   }
+
+   public boolean exists(ZipEntryHandler handler)
+   {
+      if (handler == null)
+         throw new IllegalArgumentException("Null handler");
+
+      checkIfModified();
+      String pathName = handler.getLocalPathName();
+      EntryInfo ei = entries.get(pathName);
+      if(ei == null)
+         return false;
+
+      if (ei.entry == null && pathName.length() == 0)
+         return zipSource.exists();
+
+      return true;
+   }
+
+   public boolean isLeaf(ZipEntryHandler handler)
+   {
+      if (handler == null)
+         throw new IllegalArgumentException("Null handler");
+
+      checkIfModified();
+      EntryInfo ei = entries.get(handler.getLocalPathName());
+      if(ei == null || ei.entry == null)
+         return false;
+
+      return !ei.entry.isDirectory();
+   }
+
+   public InputStream openStream(ZipEntryHandler handler) throws IOException
+   {
+      if (handler == null)
+         throw new IllegalArgumentException("Null handler");
+
+      checkIfModified();
+      EntryInfo ei = entries.get(handler.getLocalPathName());
+
+      if (ei == null)
+      {
+         String uriStr = "";
+         try
+         {
+            uriStr = handler.toURI().toString();
+         }
+         catch(Exception ex)
+         {
+            throw new RuntimeException("ASSERTION ERROR - uri generation failed for ZipEntryHandler: " + handler, ex);
+         }
+         throw new FileNotFoundException(uriStr);
+
+      }
+
+      if(ei.entry == null)
+      {
+         return zipSource.getRootAsStream();
+      }
+      
+      return zipSource.openStream(ei.entry);
+   }
+
+   public void addChild(AbstractVirtualFileHandler parent, AbstractVirtualFileHandler child)
+   {
+      if (parent == null)
+         throw new IllegalArgumentException("Null parent");
+
+      if (child == null)
+         throw new IllegalArgumentException("Null child");
+
+      EntryInfo parentEntry = entries.get(parent.getLocalPathName());
+      if (parentEntry != null)
+         parentEntry.add(child);
+      else
+         throw new RuntimeException("Parent does not exist: " + parent);
+   }
+
+
+
+   protected void finalize()
+   {
+      try
+      {
+         super.finalize();
+         zipSource.close();
+      }
+      catch (Throwable ignored)
+      {
+         log.debug("IGNORING: Failed to close zip source: " + zipSource, ignored);
+      }
+   }
+
+
+   public void replaceChild(ZipEntryHandler parent, AbstractVirtualFileHandler original, VirtualFileHandler replacement)
+   {
+      EntryInfo parentEntry = entries.get(parent.getLocalPathName());
+      if (parentEntry != null)
+      {
+         DelegatingHandler newOne;
+
+         if (replacement instanceof DelegatingHandler)
+         {
+            newOne = (DelegatingHandler) replacement;
+         }
+         else
+         {
+            DelegatingHandler delegator = new DelegatingHandler(this, parent, original.getName(), replacement);
+            newOne = delegator;
+         }
+
+         synchronized(this)
+         {
+            parentEntry.replaceChild(original, newOne);
+
+            EntryInfo ei = entries.get(original.getLocalPathName());
+            ei.handler = newOne;
+            ei.entry = null;
+            ei.clearChildren();
+         }
+      }
+      else
+      {
+         throw new RuntimeException("Parent does not exist: " + parent);
+      }
+   }
+
+
+   /**
+    *  Internal data structure holding meta information of a virtual file in this context
+    */
+   static class EntryInfo
+   {
+      private AbstractVirtualFileHandler handler;
+      private ZipEntry entry;
+      private List<AbstractVirtualFileHandler> children;
+
+      EntryInfo(AbstractVirtualFileHandler handler, ZipEntry entry)
+      {
+         this.handler = handler;
+         this.entry = entry;
+      }
+
+      public synchronized List<VirtualFileHandler> getChildren()
+      {
+         if (children == null)
+            return Collections.emptyList();
+
+         return new LinkedList<VirtualFileHandler>(children);
+      }
+
+      public synchronized void replaceChild(AbstractVirtualFileHandler original, AbstractVirtualFileHandler replacement)
+      {
+         if (children != null)
+         {
+            int i = 0;
+            Iterator<AbstractVirtualFileHandler> it = children.iterator();
+            while(it.hasNext())
+            {
+               AbstractVirtualFileHandler child = it.next();
+               if (child.getName().equals(original.getName()))
+               {
+                  children.set(i, replacement);
+                  break;
+               }
+               i++;
+            }
+         }
+      }
+
+      public synchronized void clearChildren()
+      {
+         if (children != null)
+            children.clear();
+      }
+
+      public synchronized void add(AbstractVirtualFileHandler child)
+      {
+         if (children == null)
+         {
+            children = new LinkedList<AbstractVirtualFileHandler>();
+         }
+         else
+         {
+            // if a child exists with this name already, remove it
+            Iterator<AbstractVirtualFileHandler> it = children.iterator();
+            while (it.hasNext())
+            {
+               AbstractVirtualFileHandler handler = it.next();
+               if (handler.getName().equals(child.getName()))
+               {
+                  it.remove();
+                  break;
+               }
+            }
+         }
+
+         children.add(child);
+      }
+   }
+
+
+
+   //
+   //   Helper methods
+   //
+
+
+
+   /**
+    * Copy input stream to output stream and close them both
+    *
+    * @param is
+    * @param os
+    * @throws IOException
+    */
+   static void copyStreamAndClose(InputStream is, OutputStream os) throws IOException
+   {
+      try
+      {
+         byte [] buff = new byte[65536];
+         int count = is.read(buff);
+         while(count != -1)
+         {
+            os.write(buff, 0, count);
+            count = is.read(buff);
+         }
+      }
+      finally
+      {
+         if(is != null)
+         {
+            try {
+               is.close();
+            }
+            catch(Exception ignored)
+            {
+            }
+         }
+         os.close();
+      }
+   }
+
+   /**
+    * Make sure url protocol is <em>vfszip</em>
+    *
+    * @param rootURL
+    * @return
+    * @throws MalformedURLException
+    */
+   private static URL fixUrl(URL rootURL) throws MalformedURLException
+   {
+      if ("vfszip".equals(rootURL.getProtocol()) == false)
+      {
+         String url = rootURL.toString();
+         int pos = url.indexOf(":/");
+         if (pos != -1)
+            url = url.substring(pos);
+
+         return new URL("vfszip" + url);
+      }
+      return rootURL;
+   }
+
+   /**
+    * Break to path + name
+    *
+    * @param pathName
+    * @return
+    */
+   public static String [] splitParentChild(String pathName)
+   {
+      if(pathName.length() == 0)
+         return new String [] {null, pathName};
+
+      int toPos = pathName.length();
+      if(pathName.charAt(pathName.length()-1) == '/')
+         toPos --;
+
+      int delimPos = pathName.lastIndexOf('/', toPos-1);
+
+      String [] ret;
+      if(delimPos == -1)
+      {
+         ret = new String []
+         {
+            "",
+            pathName.substring(delimPos+1, toPos)
+         };
+      }
+      else
+      {
+         ret = new String []
+         {
+            pathName.substring(0, delimPos),
+            pathName.substring(delimPos+1, toPos)
+         };
+      }
+      return ret;
+   }
+
+
+   /**
+    * Temporary files naming scheme
+    *
+    * @param name
+    * @return
+    */
+   private static String getTempFileName(String name)
+   {
+      int delim = name.lastIndexOf("/");
+      if (delim != -1)
+         name = name.substring(delim+1);
+      return UUID.randomUUID().toString().substring(0, 8) + "_" + name;
+   }
+
+   /**
+    * Use VFS's temp directory and make 'vfs-nested' sub-directory inside it for our purposes
+    *
+    * @return
+    */
+   private static String getTempDir()
+   {
+      File dir = new File(VFSUtils.getTempDirectory(), "vfs-nested.tmp");
+      return dir.toString();
+   }
+
+   /**
+    * Delete first-level files only, don't drill down
+    */
+   private static void deleteTmpDirContents()
+   {
+      try
+      {
+         File tmpDir = new File(getTempDir());
+         File [] files = tmpDir.listFiles();
+         for (File file: files)
+         {
+            if (!file.isDirectory() && !file.isHidden())
+               file.delete();
+         }
+      }
+      catch(Exception ignored)
+      {
+      }
+
+   }
+
+
+   private static class CheckForceCopy implements PrivilegedAction<Boolean>
+   {
+      public Boolean run()
+      {
+         String forceString = System.getProperty(VFSUtils.FORCE_COPY_KEY, "false");
+         return Boolean.valueOf(forceString);
+      }
+   }
+}

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,97 +0,0 @@
-package org.jboss.virtual.plugins.context.zip;
-
-import org.jboss.virtual.plugins.context.AbstractContextFactory;
-import org.jboss.virtual.spi.VFSContext;
-
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * ContextFactory that keeps track of ZipEntryContexts
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class ZipEntryContextFactory extends AbstractContextFactory
-{
-
-   public static Map<String, ZipEntryContext> ctxCache = new ConcurrentHashMap<String, ZipEntryContext>();
-
-   private static ZipEntryContextFactory instance = new ZipEntryContextFactory();
-
-   public ZipEntryContextFactory()
-   {
-      super("jar", "vfsjar", "zip", "vfszip");
-   }
-
-   public VFSContext getVFS(URI rootURI) throws IOException
-   {
-      return getVFS(rootURI.toURL());
-   }
-
-   public VFSContext getVFS(URL rootURL) throws IOException
-   {
-      String key = rootURL.toString();
-      int cutPos = key.indexOf(":/");
-      key = key.substring(cutPos+1);
-
-      String longestMatchingKey = null;
-      ZipEntryContext longestMatchingCtx = null;
-
-      for(Map.Entry<String, ZipEntryContext> ent : ctxCache.entrySet())
-      {
-         if(key.startsWith(ent.getKey()))
-         {
-            if(longestMatchingCtx == null || ent.getKey().length() > longestMatchingKey.length())
-            {
-               longestMatchingKey = ent.getKey();
-               longestMatchingCtx = ent.getValue();
-            }
-         }
-      }
-
-      ZipEntryContext ctx = null;
-      if(longestMatchingCtx != null)
-         ctx = longestMatchingCtx;
-
-      if(ctx != null)
-         return ctx;
-
-      try
-      {
-         ctx = new ZipEntryContext(rootURL);
-      }
-      catch(URISyntaxException ex)
-      {
-         MalformedURLException e = new MalformedURLException("Failed to convert URL to URI: " + rootURL);
-         e.initCause(ex);
-         throw e;
-      }
-
-      // no need to put a newly created context into ctxCache
-      // it's constructor does that by calling registerContext
-      
-      return ctx;
-   }
-
-   public static ZipEntryContextFactory getInstance()
-   {
-      return instance;
-   }
-
-   public static void registerContext(ZipEntryContext ctx)
-   {
-      String key = ctx.getRootURI().toString();
-      int cutPos = key.indexOf(":/");
-      key = key.substring(cutPos+1);
-      if("".equals(key))
-         throw new RuntimeException("Derived key for ZipEntryContext registration is empty: " + ctx.getRootURI());
-      ctxCache.put(key, ctx);
-   }
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContextFactory.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,129 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import org.jboss.virtual.plugins.context.AbstractContextFactory;
+import org.jboss.virtual.spi.VFSContext;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * ContextFactory that keeps track of ZipEntryContexts
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class ZipEntryContextFactory extends AbstractContextFactory
+{
+   /** registry of all ZipEntryContext instances */
+   private static Map<String, ZipEntryContext> ctxCache = new ConcurrentHashMap<String, ZipEntryContext>();
+
+   /** singleton */
+   private static ZipEntryContextFactory instance = new ZipEntryContextFactory();
+
+   /**
+    * ZipEntryContextFactory registers two url protocols: <em>zip</em> and <em>vfszip</em>
+    */
+   public ZipEntryContextFactory()
+   {
+      super("zip", "vfszip");  // "jar", "vfsjar",  
+   }
+
+   public VFSContext getVFS(URI rootURI) throws IOException
+   {
+      return getVFS(rootURI.toURL());
+   }
+
+   /**
+    * Find a best matching existing ZipEntryContext, or create a new one if none matches.
+    *
+    * @param rootURL
+    * @return
+    * @throws IOException
+    */
+   public VFSContext getVFS(URL rootURL) throws IOException
+   {
+      String key = rootURL.toString();
+      int cutPos = key.indexOf(":/");
+      key = key.substring(cutPos+1);
+
+      String longestMatchingKey = null;
+      ZipEntryContext longestMatchingCtx = null;
+
+      for(Map.Entry<String, ZipEntryContext> ent : ctxCache.entrySet())
+      {
+         if(key.startsWith(ent.getKey()))
+         {
+            if(longestMatchingCtx == null || ent.getKey().length() > longestMatchingKey.length())
+            {
+               longestMatchingKey = ent.getKey();
+               longestMatchingCtx = ent.getValue();
+            }
+         }
+      }
+
+      ZipEntryContext ctx = null;
+      if(longestMatchingCtx != null)
+         ctx = longestMatchingCtx;
+
+      if(ctx != null)
+         return ctx;
+
+      try
+      {
+         ctx = new ZipEntryContext(rootURL);
+      }
+      catch(URISyntaxException ex)
+      {
+         MalformedURLException e = new MalformedURLException("Failed to convert URL to URI: " + rootURL);
+         e.initCause(ex);
+         throw e;
+      }
+
+      // ZipEntryContext registers newly created context with this factory
+      // by calling registerContext() which puts a newly created context into ctxCache
+      
+      return ctx;
+   }
+
+   public static ZipEntryContextFactory getInstance()
+   {
+      return instance;
+   }
+
+   public static void registerContext(ZipEntryContext ctx)
+   {
+      String key = ctx.getRootURI().toString();
+      int cutPos = key.indexOf(":/");
+      key = key.substring(cutPos+1);
+      if("".equals(key))
+         throw new RuntimeException("Derived key for ZipEntryContext registration is empty: " + ctx.getRootURI());
+      ctxCache.put(key, ctx);
+   }
+}

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,122 +0,0 @@
-package org.jboss.virtual.plugins.context.zip;
-
-import org.jboss.virtual.VFSUtils;
-import org.jboss.virtual.plugins.context.AbstractVirtualFileHandler;
-import org.jboss.virtual.plugins.context.StructuredVirtualFileHandler;
-import org.jboss.virtual.spi.VirtualFileHandler;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Handler representing an individual file (ZipEntry) within ZipEntryContext
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class ZipEntryHandler extends AbstractVirtualFileHandler implements StructuredVirtualFileHandler
-{
-
-   /** The url */
-   private final URL url;
-
-
-   /**
-    * Create a new ZipEntryHandler.
-    *
-    * @param context ZipEntryContext
-    * @param parent  parent within the same context
-    * @param name    name of this file within context
-    */
-   public ZipEntryHandler(ZipEntryContext context, AbstractVirtualFileHandler parent, String name) throws IOException
-   {
-      super(context, parent, name);
-
-      url = context.getChildURL(parent, name);
-
-      String vfsUrl = url.toString();
-      int pos = vfsUrl.indexOf(":/");
-      vfsUrl = "vfszip:" + vfsUrl.substring(pos+1);
-      try
-      {
-         setVfsUrl(new URL(vfsUrl));
-      }
-      catch(MalformedURLException ex)
-      {
-         throw new RuntimeException("ASSERTION ERROR - failed to set vfsUrl: " + vfsUrl, ex );
-      }
-
-      if(parent != null)
-      {
-         context.addChild(parent, this);
-      }
-   }
-
-   public URI toURI() throws URISyntaxException
-   {
-      return VFSUtils.toURI(url);
-   }
-
-   public long getLastModified() throws IOException
-   {
-      return getZipEntryContext().getLastModified(this);
-   }
-
-   public long getSize() throws IOException
-   {
-      return getZipEntryContext().getSize(this);
-   }
-
-   public boolean exists() throws IOException
-   {
-      return getZipEntryContext().exists(this);
-   }
-
-   public boolean isLeaf() throws IOException
-   {
-      return getZipEntryContext().isLeaf(this);
-   }
-
-   public boolean isHidden() throws IOException
-   {
-      return false;
-   }
-
-   public InputStream openStream() throws IOException
-   {
-      return getZipEntryContext().openStream(this);
-   }
-
-   public List<VirtualFileHandler> getChildren(boolean ignoreErrors) throws IOException
-   {
-      return getZipEntryContext().getChildren(this, ignoreErrors);
-
-   }
-
-   public VirtualFileHandler getChild(String path) throws IOException
-   {
-      return structuredFindChild(path);
-   }
-
-
-   public VirtualFileHandler createChildHandler(String name) throws IOException
-   {
-      return getZipEntryContext().getChild(this, name);
-   }
-
-   private ZipEntryContext getZipEntryContext()
-   {
-      return ((ZipEntryContext) getVFSContext());
-   }
-
-
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryHandler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,158 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import org.jboss.virtual.VFSUtils;
+import org.jboss.virtual.plugins.context.AbstractVirtualFileHandler;
+import org.jboss.virtual.plugins.context.StructuredVirtualFileHandler;
+import org.jboss.virtual.spi.VirtualFileHandler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Handler representing an individual file (ZipEntry) within ZipEntryContext
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class ZipEntryHandler extends AbstractVirtualFileHandler implements StructuredVirtualFileHandler
+{
+
+   /** The url */
+   private final URL url;
+
+
+   /**
+    * Create a new ZipEntryHandler.
+    *
+    * @param context ZipEntryContext
+    * @param parent  parent within the same context
+    * @param name    name of this file within context
+    * @param isLeaf  true if this file should have a URL not ending with '/', false otherwise
+    */
+   public ZipEntryHandler(ZipEntryContext context, AbstractVirtualFileHandler parent, String name, boolean isLeaf) throws IOException
+   {
+      super(context, parent, name);
+
+      url = context.getChildURL(parent, name);
+
+      String currentUrl = url.toString();
+      int pos = currentUrl.indexOf(":/");
+      StringBuilder vfsUrl = new StringBuilder();
+      vfsUrl.append("vfszip:").append(currentUrl.substring(pos+1));
+      try
+      {
+         if (isLeaf == false && vfsUrl.charAt(vfsUrl.length()-1) != '/')
+            vfsUrl.append("/");
+         setVfsUrl(new URL(vfsUrl.toString()));
+      }
+      catch(MalformedURLException ex)
+      {
+         throw new RuntimeException("ASSERTION ERROR - failed to set vfsUrl: " + vfsUrl, ex );
+      }
+
+      if(parent != null)
+      {
+         context.addChild(parent, this);
+      }
+   }
+
+   public URI toURI() throws URISyntaxException
+   {
+      return VFSUtils.toURI(url);
+   }
+
+   public long getLastModified() throws IOException
+   {
+      return getZipEntryContext().getLastModified(this);
+   }
+
+   public long getSize() throws IOException
+   {
+      return getZipEntryContext().getSize(this);
+   }
+
+   public boolean exists() throws IOException
+   {
+      return getZipEntryContext().exists(this);
+   }
+
+   public boolean isLeaf() throws IOException
+   {
+      checkClosed();
+      return getZipEntryContext().isLeaf(this);
+   }
+
+   public boolean isHidden() throws IOException
+   {
+      checkClosed();
+      return false;
+   }
+
+   public InputStream openStream() throws IOException
+   {
+      checkClosed();
+      return getZipEntryContext().openStream(this);
+   }
+
+   public List<VirtualFileHandler> getChildren(boolean ignoreErrors) throws IOException
+   {
+      return getZipEntryContext().getChildren(this, ignoreErrors);
+
+   }
+
+   public VirtualFileHandler getChild(String path) throws IOException
+   {
+      return structuredFindChild(path);
+   }
+
+
+   public VirtualFileHandler createChildHandler(String name) throws IOException
+   {
+      return getZipEntryContext().getChild(this, name);
+   }
+
+   protected void internalReplaceChild(VirtualFileHandler original, VirtualFileHandler replacement)
+   {
+      if (original instanceof AbstractVirtualFileHandler == false)
+         throw new IllegalArgumentException("Original file handler not found in this context: " + original);
+
+      getZipEntryContext().replaceChild(this, (AbstractVirtualFileHandler) original, replacement);
+   }
+
+   private ZipEntryContext getZipEntryContext()
+   {
+      return ((ZipEntryContext) getVFSContext());
+   }
+
+
+}

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,143 +0,0 @@
-package org.jboss.virtual.plugins.context.zip;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.ZipEntry;
-
-/**
- * InputStream that wraps an InputStream retrieved from ZipFile.getInputStream(entry)
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class ZipEntryInputStream extends InputStream
-{
-   private InputStream delegate;
-
-   private ZipFileWrapper zipWrapper;
-
-   private boolean closed;
-
-   ZipEntryInputStream(ZipFileWrapper zipWrapper, InputStream is) throws IOException
-   {
-      this.zipWrapper = zipWrapper;
-      delegate = is;
-   }
-
-   private void streamClosed(boolean doClose)
-   {
-      if (closed == false && doClose)
-      {
-         closed = true;
-         zipWrapper.release();
-      }
-   }
-
-   public int read() throws IOException
-   {
-      int rc = -1;
-      try
-      {
-         rc = delegate.read();
-         return rc;
-      }
-      finally
-      {
-         streamClosed(rc < 0);
-      }
-   }
-
-   public int read(byte buf[]) throws IOException
-   {
-      int rc = -1;
-      try
-      {
-         rc = delegate.read(buf);
-         return rc;
-      }
-      finally
-      {
-         streamClosed(rc < 0);
-      }
-   }
-
-   public int read(byte buf[], int off, int len) throws IOException
-   {
-      int rc = -1;
-      try
-      {
-         rc = delegate.read(buf, off, len);
-         return rc;
-      }
-      finally
-      {
-         streamClosed(rc < 0);
-      }
-   }
-
-   public synchronized void reset() throws IOException
-   {
-      boolean ok = false;
-      try
-      {
-         delegate.reset();
-         ok = true;
-      }
-      finally
-      {
-         streamClosed(ok == false);
-      }
-   }
-
-   public synchronized void mark(int readlimit)
-   {
-      boolean ok = false;
-      try
-      {
-         delegate.mark(readlimit);
-         ok = true;
-      }
-      finally
-      {
-         streamClosed(ok == false);
-      }
-   }
-
-   public int available() throws IOException
-   {
-      boolean ok = false;
-      try
-      {
-         int ret = delegate.available();
-         ok = true;
-         return ret;
-      }
-      finally
-      {
-         streamClosed(ok == false);
-      }
-   }
-
-   public long skip(long n) throws IOException
-   {
-      boolean ok = false;
-      try
-      {
-         long ret = delegate.skip(n);
-         ok = true;
-         return ret;
-      }
-      finally
-      {
-         streamClosed(ok == false);
-      }
-   }
-
-   public void close() throws IOException
-   {
-      streamClosed(true);
-      super.close();
-   }
-
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryInputStream.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,185 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ZipEntryInputStream is part of ZipFileWrapper implementation.
+ *
+ * It wraps the stream retrieved from ZipFile.getInputStream(entry)
+ * and releases the underlying ZipFileWrapper when detecting end of use.
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class ZipEntryInputStream extends InputStream
+{
+   private InputStream delegate;
+
+   private ZipFileWrapper zipWrapper;
+
+   private boolean closed;
+
+   ZipEntryInputStream(ZipFileWrapper zipWrapper, InputStream is) throws IOException
+   {
+      if (is == null)
+         throw new IllegalArgumentException("Input stream is null");
+      
+      this.zipWrapper = zipWrapper;
+      delegate = is;
+   }
+
+   private void streamClosed(boolean doClose)
+   {
+      if (closed == false && doClose)
+      {
+         closed = true;
+         zipWrapper.release();
+      }
+   }
+
+   public int read() throws IOException
+   {
+      int rc = -1;
+      try
+      {
+         rc = delegate.read();
+         return rc;
+      }
+      finally
+      {
+         streamClosed(rc < 0);
+      }
+   }
+
+   public int read(byte buf[]) throws IOException
+   {
+      int rc = -1;
+      try
+      {
+         rc = delegate.read(buf);
+         return rc;
+      }
+      finally
+      {
+         streamClosed(rc < 0);
+      }
+   }
+
+   public int read(byte buf[], int off, int len) throws IOException
+   {
+      int rc = -1;
+      try
+      {
+         rc = delegate.read(buf, off, len);
+         return rc;
+      }
+      finally
+      {
+         streamClosed(rc < 0);
+      }
+   }
+
+   public synchronized void reset() throws IOException
+   {
+      boolean ok = false;
+      try
+      {
+         delegate.reset();
+         ok = true;
+      }
+      finally
+      {
+         streamClosed(ok == false);
+      }
+   }
+
+   public synchronized void mark(int readlimit)
+   {
+      boolean ok = false;
+      try
+      {
+         delegate.mark(readlimit);
+         ok = true;
+      }
+      finally
+      {
+         streamClosed(ok == false);
+      }
+   }
+
+   public int available() throws IOException
+   {
+      boolean ok = false;
+      try
+      {
+         int ret = delegate.available();
+         ok = true;
+         return ret;
+      }
+      finally
+      {
+         streamClosed(ok == false);
+      }
+   }
+
+   public long skip(long n) throws IOException
+   {
+      boolean ok = false;
+      try
+      {
+         long ret = delegate.skip(n);
+         ok = true;
+         return ret;
+      }
+      finally
+      {
+         streamClosed(ok == false);
+      }
+   }
+
+   public void close() throws IOException
+   {
+      streamClosed(true);
+      super.close();
+   }
+
+   protected void finalize()
+   {
+      try
+      {
+         close();
+      }
+      catch(IOException ignored)
+      {
+      }
+   }
+
+   boolean isClosed()
+   {
+      return closed;
+   }
+
+}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileLockReaper.java (from rev 73688, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileLockReaper.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileLockReaper.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileLockReaper.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,168 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import org.jboss.logging.Logger;
+
+import java.util.Iterator;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * A monitoring object that closes ZipFiles when they haven't been used for a while
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class ZipFileLockReaper
+{
+   private static final Logger log = Logger.getLogger(ZipFileLockReaper.class);
+
+   /**
+    * Time after which unused ZipFiles can be closed. This shouldn't be a large number
+    * to ensure smooth releasing of file locks
+    */
+   private static final int PERIOD = 5000;
+
+   /** Timer thread period */
+   private static final int TIMER_PERIOD = 1000;
+
+   /** If timer finds out there haven't been any ZipFiles open for a while it shuts down until some are (re)opened */
+   private static final int TIMER_UNUSED_PERIOD = 30000;
+
+   /** There is only one instance that serves all ZipFileWrappers */
+   private static ZipFileLockReaper singleton;
+
+   /** A list of monitored ZipFileWrappers */
+   private ConcurrentLinkedQueue monitored = new ConcurrentLinkedQueue();
+
+   /** The number of monitored ZipFileWrappers */
+   private int monitoredCount = 0;
+
+   /** Timer used for actual reaping - async closure of ZipFiles */
+   private Timer timer;
+
+   /** Timestamp of last unregister() call */
+   private long lastUsed;
+
+
+   /**
+    * Private constructor - to force retrieval through {@link #getInstance()}
+    */
+   private ZipFileLockReaper()
+   {
+
+   }
+
+   /** Factory method to be used to retrieve reference to ZipFileLockReaper */
+   public synchronized static ZipFileLockReaper getInstance()
+   {
+      if (singleton == null)
+         singleton = new ZipFileLockReaper();
+
+      return singleton;
+   }
+
+
+   /** Register a ZipFileWrapper instance with this reaper */
+   public synchronized void register(ZipFileWrapper w)
+   {
+      monitored.add(w);
+      monitoredCount++;
+      if (timer == null)
+      {
+         timer = new Timer("ZipFile Lock Reaper", true);
+         timer.schedule(new ReaperTimerTask(), TIMER_PERIOD, TIMER_PERIOD);
+      }
+      log.debug("Registered: " + w);
+   }
+
+   /** Unregister a ZipFileWrapper instance from this reaper */
+   public synchronized void unregister(ZipFileWrapper w)
+   {
+      monitored.remove(w);
+      monitoredCount--;
+      lastUsed = System.currentTimeMillis();
+      log.debug("Unregistered: " + w);
+   }
+
+
+
+   /** Timer task that does the actual reaping */
+   class ReaperTimerTask extends TimerTask
+   {
+      public void run()
+      {
+         log.debug("Timer called");
+
+         long now = System.currentTimeMillis();
+         synchronized (ZipFileLockReaper.this)
+         {
+            if (monitoredCount == 0)
+            {
+               if (now - lastUsed > TIMER_UNUSED_PERIOD)
+               {
+                  timer.cancel();
+                  timer = null;
+                  log.debug("Cancelled the timer");
+               }
+               return;
+            }
+         }
+
+         Iterator it = monitored.iterator();
+         while (it.hasNext())
+         {
+            ZipFileWrapper w = (ZipFileWrapper) it.next();
+
+            // stream leak debug
+            /*
+            Iterator<ZipEntryInputStream> sit = w.streams.iterator();
+            while (sit.hasNext())
+            {
+               ZipEntryInputStream eis = sit.next();
+               if (!eis.isClosed())
+               {
+                  System.out.println("Stream not closed: " + eis.debugCount + " - " + eis);
+               }
+            }
+            */
+
+            if (w.getReferenceCount() <= 0 && now - w.getLastUsed() > PERIOD)
+            {
+               try
+               {
+                  w.closeZipFile();
+                  log.debug("Asynchronously closed an unused ZipFile: " + w);
+               }
+               catch(Exception ignored)
+               {
+                  log.debug("IGNORING: Failed to close ZipFile: " + w, ignored);
+               }
+            }
+         }
+      }
+   }
+
+}

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,125 +0,0 @@
-package org.jboss.virtual.plugins.context.zip;
-
-import org.jboss.logging.Logger;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-/**
- * ZipFileWrapper releases and reacquires an underlying ZipFile as necessary
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class ZipFileWrapper
-{
-   private static final Logger log = Logger.getLogger(ZipFileWrapper.class);
-
-   private File file;
-
-   private ZipFile zipFile;
-
-   private long lastUsed;
-
-   private long lastModified;
-
-   private long lastChecked;
-
-   private int refCount = 0;
-
-   public ZipFileWrapper(File archive)
-   {
-      file = archive;
-      lastModified = file.lastModified();
-   }
-
-   boolean hasBeenModified()
-   {
-      long now = System.currentTimeMillis();
-      if (now - lastChecked < 1000)
-         return false;
-
-      lastChecked = now;
-      long lm = file.lastModified();
-      if (lm != lastModified)
-      {
-         lastModified = lm;
-         return true;
-      }
-
-      return false;
-   }
-
-   boolean exists()
-   {
-      return file.isFile();
-   }
-
-   private ZipFile ensureZipFile() throws IOException
-   {
-      if (zipFile == null)
-         zipFile = new ZipFile(file);
-
-      return zipFile;
-   }
-
-   private void closeZipFile() throws IOException
-   {
-      if (zipFile != null)
-      {
-         ZipFile zf = zipFile;
-         zipFile = null;
-         zf.close();
-      }
-   }
-
-   synchronized ZipEntryInputStream openStream(ZipEntry ent) throws IOException
-   {
-      ensureZipFile();
-      InputStream is = zipFile.getInputStream(ent);
-      ZipEntryInputStream zis = new ZipEntryInputStream(this, is);
-      refCount++;
-      lastUsed = System.currentTimeMillis();
-      return zis;
-   }
-
-
-   synchronized void release()
-   {
-      refCount--;
-      if (refCount <= 0)
-      {
-         try
-         {
-            lastUsed = System.currentTimeMillis();
-            closeZipFile();
-         }
-         catch(Exception ex)
-         {
-            log.warn("Failed to release file: " + file);
-         }
-      }
-   }
-
-   synchronized ZipFile acquire() throws IOException
-   {
-      ZipFile ret = ensureZipFile();
-      refCount++;
-      return ret;
-   }
-
-   void close()
-   {
-      try
-      {
-         closeZipFile();
-      }
-      catch(Exception ex)
-      {
-         log.warn("Failed to release file: " + file);
-      }
-   }
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipFileWrapper.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,232 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import org.jboss.logging.Logger;
+import org.jboss.virtual.VFSUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+
+/**
+ * ZipFileWrapper - for abstracted access to zip files on disk
+ *
+ * It releases and reacquires the underlying ZipFile as necessary
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+class ZipFileWrapper extends ZipWrapper
+{
+   private static final Logger log = Logger.getLogger(ZipFileWrapper.class);
+
+
+   private static boolean forceNoReaper;
+
+   static
+   {
+      forceNoReaper = AccessController.doPrivileged(new CheckNoReaper());
+
+      if (forceNoReaper)
+         log.info("VFS forced no-reaper-mode is enabled.");
+
+   }
+
+   private File file;
+
+   /** zip inflater wrapped around file */
+   private ZipFile zipFile;
+
+   /** true for extracted nested jars that we want removed when this wrapper is closed */
+   private boolean autoClean;
+
+   /** true if noReaper mode is forced on a per-instance basis */
+   private boolean noReaperOverride;
+
+   // used for debugging stream leaks
+   //ConcurrentLinkedQueue<ZipEntryInputStream> streams = new ConcurrentLinkedQueue<ZipEntryInputStream>();
+
+
+   ZipFileWrapper(File archive, boolean autoClean, boolean noReaperOverride)
+   {
+      this.noReaperOverride = noReaperOverride;
+      init(archive, autoClean);
+   }
+
+
+   ZipFileWrapper(URI rootPathURI, boolean autoClean, boolean noReaperOverride)
+   {
+      this.noReaperOverride = noReaperOverride;
+      File rootFile = new File(rootPathURI);
+      if(!rootFile.isFile())
+         throw new RuntimeException("File not found: " + rootFile);
+
+      init(rootFile, autoClean);
+   }
+
+   /**
+    * Extra initialization that didn't fit in constructors
+    *
+    * @param archive
+    * @param autoClean
+    */
+   private void init(File archive, boolean autoClean)
+   {
+      file = archive;
+      lastModified = file.lastModified();
+      this.autoClean = autoClean;
+      if (autoClean)
+         file.deleteOnExit();
+   }
+
+   boolean exists()
+   {
+      return file.isFile();
+   }
+
+   long getLastModified()
+   {
+      return file.lastModified();
+   }
+
+   String getName()
+   {
+      return file.getName();
+   }
+
+   long getSize()
+   {
+      return file.length();
+   }
+
+   private ZipFile ensureZipFile() throws IOException
+   {
+      if (zipFile == null)
+      {
+         zipFile = new ZipFile(file);
+         if (forceNoReaper == false && noReaperOverride == false)
+            ZipFileLockReaper.getInstance().register(this);
+      }
+
+      return zipFile;
+   }
+
+   synchronized void closeZipFile() throws IOException
+   {
+      if (zipFile != null && getReferenceCount() <= 0)
+      {
+         ZipFile zf = zipFile;
+         zipFile = null;
+         zf.close();
+         if (forceNoReaper == false && noReaperOverride == false)
+            ZipFileLockReaper.getInstance().unregister(this);
+      }
+   }
+
+   synchronized InputStream openStream(ZipEntry ent) throws IOException
+   {
+      ensureZipFile();
+      InputStream is = zipFile.getInputStream(ent);
+      if (is == null)
+         throw new IOException("Entry no longer available: " + ent.getName() + " in file " + file);
+      
+      ZipEntryInputStream zis = new ZipEntryInputStream(this, is);
+
+      // debugging code
+      //streams.add(zis);
+
+      incrementRef();
+      return zis;
+   }
+
+
+   InputStream getRootAsStream() throws FileNotFoundException
+   {
+      return new FileInputStream(file);
+   }
+
+
+   synchronized void acquire() throws IOException
+   {
+      ensureZipFile();
+      incrementRef();
+   }
+
+   synchronized void release() {
+      super.release();
+      if (forceNoReaper || noReaperOverride)
+         try
+         {
+            closeZipFile();
+         }
+         catch(Exception ex)
+         {
+            log.warn("Failed to release file: " + file);
+         }
+   }
+
+   synchronized Enumeration<? extends ZipEntry> entries() throws IOException
+   {
+      return ensureZipFile().entries();
+   }
+
+   void close()
+   {
+      try
+      {
+         closeZipFile();
+      }
+      catch(Exception ignored)
+      {
+         log.warn("IGNORING: Failed to release file: " + file, ignored);
+      }
+
+      if (autoClean)
+         file.delete();
+   }
+
+   public String toString()
+   {
+      return super.toString() + " - " + file.getAbsolutePath();
+   }
+
+
+   private static class CheckNoReaper implements PrivilegedAction<Boolean>
+   {
+      public Boolean run()
+      {
+         String forceString = System.getProperty(VFSUtils.FORCE_NO_REAPER_KEY, "false");
+         return Boolean.valueOf(forceString);
+      }
+   }
+}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java (from rev 73900, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,163 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * ZipStreamWrapper - for abstracted access to in-memory zip file
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+class ZipStreamWrapper extends ZipWrapper
+{
+   /** Raw zip archive loaded in memory */
+   private byte [] zipBytes;
+
+   /** Name */
+   private String name;
+
+   /**
+    * ZipStreamWrapper is not aware of actual zip source so it can not detect
+    * if it's been modified, like ZipFileWrapper does.
+    *
+    * @param zipStream
+    * @param lastModified passed by zip stream provider - constant value
+    * @throws IOException
+    */
+   ZipStreamWrapper(InputStream zipStream, String name, long lastModified) throws IOException
+   {
+      // read the contents into memory buffer
+      ByteArrayOutputStream bout = new ByteArrayOutputStream();
+      ZipEntryContext.copyStreamAndClose(zipStream, bout);
+      zipBytes = bout.toByteArray();
+
+      // TODO - delegate file meta info operations to parent?
+      this.name = name;
+      this.lastModified = lastModified;
+   }
+
+   boolean exists()
+   {
+      return true;
+   }
+
+   long getLastModified()
+   {
+      return lastModified;
+   }
+
+   String getName()
+   {
+      return name;
+   }
+
+   long getSize()
+   {
+      return zipBytes.length;
+   }
+
+   InputStream openStream(ZipEntry ent) throws IOException
+   {
+      ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes));
+
+      // first find the entry
+      ZipEntry entry = zis.getNextEntry();
+      while(entry != null)
+      {
+         if(entry.getName().equals(ent.getName()))
+            break;
+         entry = zis.getNextEntry();
+      }
+      if(entry == null)
+         throw new IOException("Failed to find nested jar entry: " + ent.getName() + " in zip stream: " + this.name);
+
+
+      // then read it
+      return new SizeLimitedInputStream(zis, (int) entry.getSize());
+   }
+
+   InputStream getRootAsStream() throws FileNotFoundException
+   {
+      return new ByteArrayInputStream(zipBytes);
+   }
+
+   void acquire() throws IOException
+   {
+   }
+
+   Enumeration<? extends ZipEntry> entries() throws IOException
+   {
+      return new ZipStreamEnumeration(new ZipInputStream(new ByteArrayInputStream(zipBytes)));
+   }
+
+
+   void close()
+   {
+      zipBytes = null;
+   }
+
+   public String toString()
+   {
+      return super.toString() + " - " + name;
+   }
+
+
+
+   class ZipStreamEnumeration implements Enumeration
+   {
+      private ZipInputStream zis;
+
+      private ZipEntry entry;
+
+      ZipStreamEnumeration(ZipInputStream zis) throws IOException
+      {
+         this.zis = zis;
+         entry = zis.getNextEntry();
+      }
+
+      public boolean hasMoreElements()
+      {
+         return entry != null;
+      }
+
+      public Object nextElement()
+      {
+         Object ret = entry;
+         try
+         {
+            entry = zis.getNextEntry();
+         }
+         catch (IOException ex)
+         {
+            throw new RuntimeException("Failed to retrieve next entry from zip stream", ex);
+         }
+
+         return ret;
+      }
+   }
+
+}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipWrapper.java (from rev 73900, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/plugins/context/zip/ZipWrapper.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipWrapper.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipWrapper.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,123 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.virtual.plugins.context.zip;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileNotFoundException;
+import java.util.zip.ZipEntry;
+import java.util.Enumeration;
+
+/**
+ * ZipWrapper represents abstracted access to zip archive
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+abstract class ZipWrapper
+{
+
+   /** last known modifyTime of the zip source */
+   protected long lastModified;
+
+   /** timestamp of last call to getLastModified()
+    * it's an expensive call - we don't do it more then once every second */
+   private long lastChecked;
+
+   /** last known activity */
+   private long lastUsed;
+
+   /** number of streams currently open on this wrapper */
+   private int refCount = 0;
+
+
+
+   /**
+    * Returns true if underlying source's lastModified time has changed since previous call
+    */
+   boolean hasBeenModified()
+   {
+      long now = System.currentTimeMillis();
+      if (now - lastChecked < 1000)
+         return false;
+
+      lastChecked = now;
+      long lm = getLastModified();
+      if (lm != lastModified)
+      {
+         lastModified = lm;
+         return true;
+      }
+
+      return false;
+   }
+
+   long getLastUsed()
+   {
+      return lastUsed;
+   }
+
+   /**
+    * Returns the number of streams currently open
+    *
+    * @return
+    */
+   int getReferenceCount()
+   {
+      return refCount;
+   }
+
+   void incrementRef()
+   {
+      refCount++;
+      lastUsed = System.currentTimeMillis();
+   }
+
+
+   synchronized void release()
+   {
+      refCount--;
+      if (refCount <= 0)
+      {
+         lastUsed = System.currentTimeMillis();
+      }
+   }
+
+   abstract void acquire() throws IOException;
+
+   abstract long getLastModified();
+
+   abstract String getName();
+
+   abstract boolean exists();
+
+   abstract long getSize();
+   
+   abstract Enumeration<? extends ZipEntry> entries() throws IOException;
+
+   abstract InputStream openStream(ZipEntry ent) throws IOException;
+
+   abstract InputStream getRootAsStream() throws FileNotFoundException;
+
+   abstract void close();
+}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/protocol/vfszip)

Deleted: projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java
===================================================================
--- projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java	2008-04-29 22:30:10 UTC (rev 72878)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -1,37 +0,0 @@
-package org.jboss.virtual.protocol.vfszip;
-
-import org.jboss.virtual.VirtualFile;
-import org.jboss.virtual.plugins.context.zip.ZipEntryContext;
-import org.jboss.virtual.plugins.context.zip.ZipEntryContextFactory;
-import org.jboss.virtual.plugins.vfs.VirtualFileURLConnection;
-
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-
-/**
- * URLStreamHandler for VFS
- *
- * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
- * @version $Revision: 1.0 $
- */
-
-public class Handler extends URLStreamHandler
-{
-   protected URLConnection openConnection(URL u) throws IOException
-   {
-      String url = u.toString();
-      ZipEntryContext ctx = (ZipEntryContext) ZipEntryContextFactory.getInstance().getVFS(u);
-      if (ctx == null)
-         throw new IOException("vfs does not exist: " + url);
-
-      String rootPath = ctx.getRootURI().getPath();
-      String entryPath = u.getFile().substring(rootPath.length());
-      VirtualFile vf = ctx.getChild(ctx.getRoot(), entryPath).getVirtualFile();
-      if (vf == null)
-         throw new IOException("vfs does not exist: " + url);
-
-      return new VirtualFileURLConnection(u, vf);
-   }
-}

Copied: projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java)
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java	                        (rev 0)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/protocol/vfszip/Handler.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,40 @@
+package org.jboss.virtual.protocol.vfszip;
+
+import org.jboss.virtual.VirtualFile;
+import org.jboss.virtual.spi.VirtualFileHandler;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContext;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContextFactory;
+import org.jboss.virtual.plugins.vfs.VirtualFileURLConnection;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+/**
+ * URLStreamHandler for VFS
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+
+public class Handler extends URLStreamHandler
+{
+   protected URLConnection openConnection(URL u) throws IOException
+   {
+      String url = u.toString();
+      ZipEntryContext ctx = (ZipEntryContext) ZipEntryContextFactory.getInstance().getVFS(u);
+      if (ctx == null)
+         throw new IOException("No VFS context found for URL: " + url);
+
+      String rootPath = ctx.getRootURI().getPath();
+      String entryPath = u.getFile().substring(rootPath.length());
+      
+      VirtualFileHandler child = ctx.getChild(ctx.getRoot(), entryPath);
+      VirtualFile vf = child == null ? null : child.getVirtualFile();
+      if (vf == null)
+         throw new IOException("No VFS file found for URL: " + url);
+
+      return new VirtualFileURLConnection(u, vf);
+   }
+}

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContext.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContext.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContext.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -68,6 +68,14 @@
    VirtualFileHandler getRoot() throws IOException;
 
    /**
+    * Return the peer representing the root of this context within another context.
+    * Used when mounting contexts within other contexts
+    *
+    * @return the root peer
+    */
+   VirtualFileHandler getRootPeer();
+
+   /**
     * Get the context option settings
     * 
     * @return a map of the context options

Modified: projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContextFactoryLocator.java
===================================================================
--- projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContextFactoryLocator.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/spi/VFSContextFactoryLocator.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -41,6 +41,7 @@
 import org.jboss.virtual.plugins.context.file.FileSystemContextFactory;
 import org.jboss.virtual.plugins.context.jar.JarContextFactory;
 import org.jboss.virtual.plugins.context.memory.MemoryContextFactory;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContextFactory;
 import org.jboss.virtual.plugins.context.VfsArchiveBrowserFactory;
 import org.jboss.util.file.ArchiveBrowser;
 
@@ -250,6 +251,9 @@
       if (factoryByProtocol.containsKey("vfsmemory") == false)
          registerFactory(MemoryContextFactory.getInstance());
 
+      if (factoryByProtocol.containsKey("vfszip") == false)
+         registerFactory(ZipEntryContextFactory.getInstance());
+      
       initialized = true;
    }
    

Modified: projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/AbstractVFSContextTest.java
===================================================================
--- projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/AbstractVFSContextTest.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/AbstractVFSContextTest.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -67,7 +67,7 @@
 
       URI uri = new URI("vfs" + rootURI);
       URI rfUri = rootFile.toURI();
-      assertEquals(uri, rfUri);
+      assertEquals(uri.getPath(), rfUri.getPath());
    }
    
    public void testGetRoot() throws Exception

Modified: projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java
===================================================================
--- projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/FileVFSUnitTestCase.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -864,7 +864,7 @@
       assertEquals("pathName", vfsPath, tmpVF.getPathName());
       assertEquals("lastModified", lastModified, tmpVF.getLastModified());
       assertEquals("size", size, tmpVF.getSize());
-      assertEquals("url", url, tmpVF.toURL());
+      assertEquals("url", url.getPath(), tmpVF.toURL().getPath());
       // TODO: these should pass
       //assertEquals("isFile", true, tmpVF.isFile());
       //assertEquals("isDirectory", false, tmpVF.isDirectory());
@@ -885,7 +885,7 @@
       assertEquals("pathName", vfsPath, tmpVF2.getPathName());
       assertEquals("lastModified", lastModified, tmpVF2.getLastModified());
       assertEquals("size", size, tmpVF2.getSize());
-      assertEquals("url", url, tmpVF2.toURL());
+      assertEquals("url", url.getPath(), tmpVF2.toURL().getPath());
       // TODO: these should pass
       //assertEquals("isFile", true, tmpVF2.isFile());
       //assertEquals("isDirectory", false, tmpVF2.isDirectory());
@@ -1052,7 +1052,7 @@
       assertTrue("jar1URL path ends in unpacked-outer.jar/jar1.jar!/",
             jar1URL.getPath().endsWith("unpacked-outer.jar/jar1.jar"));
       VirtualFile jar1 = outerJar.findChild("jar1.jar");
-      assertEquals(jar1URL, jar1.toURL());
+      assertEquals(jar1URL.getPath(), jar1.toURL().getPath());
 
       VirtualFile packedJar = vfs.findChild("jar1.jar");
       jar1URL = packedJar.findChild("org/jboss/test/vfs/support").toURL();
@@ -1081,7 +1081,7 @@
       assertTrue("jar1URI path ends in unpacked-outer.jar/jar1.jar!/",
             jar1URI.getPath().endsWith("unpacked-outer.jar/jar1.jar"));
       VirtualFile jar1 = outerJar.findChild("jar1.jar");
-      assertEquals(jar1URI, jar1.toURI());
+      assertEquals(jar1URI.getPath(), jar1.toURI().getPath());
 
       VirtualFile packedJar = vfs.findChild("jar1.jar");
       jar1URI = packedJar.findChild("org/jboss/test/vfs/support").toURI();
@@ -1244,7 +1244,7 @@
       assertNotNull("tstjar != null", tstjar);
       URI uri = tstjar.toURI();
       URI expectedURI = new URI("vfs"+rootURL.toString()+"/path%20with%20spaces/tst.jar");
-      assertEquals(uri, expectedURI);
+      assertEquals(uri.getPath(), expectedURI.getPath());
    }
 
    public static void main(String[] args) throws Exception

Modified: projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/UnpackTestCase.java
===================================================================
--- projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/UnpackTestCase.java	2008-06-01 16:17:45 UTC (rev 73900)
+++ projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/UnpackTestCase.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -119,7 +119,8 @@
       assertReplacement(original, replacement);
       VirtualFile parent = original.getParent();
       VirtualFile child = parent.findChild("level2.zip");
-      assertEquals(replacement, child);
+      //assertEquals(replacement, child);
+      assertEquals(replacement.toURI(), child.toURI());
 
       VirtualFile textTwo = replacement.findChild("test2.txt");
       testText(textTwo);
@@ -140,16 +141,19 @@
       assertReplacement(original, replacement);
       VirtualFile parent = original.getParent();
       VirtualFile child = parent.findChild("level3.zip");
-      assertEquals(replacement, child);
+      //assertEquals(replacement, child);
+      assertEquals(replacement.toURI(), child.toURI());
 
       VirtualFile textThree = replacement.findChild("test3.txt");
       testText(textThree);
    }
 
-   protected void assertReplacement(VirtualFile original, VirtualFile replacement) throws IOException
+   protected void assertReplacement(VirtualFile original, VirtualFile replacement) throws Exception
    {
       assertEquals(original.getName(), replacement.getName());
-      assertEquals(original.getPathName(), replacement.getPathName());
+      // when mounting via DelegatingHandler, getPathName changes because VFSContext changes
+      //assertEquals(original.getPathName(), replacement.getPathName());
+      
       // it's a directory
       assertEquals(0, replacement.getSize());
       assertEquals(original.exists(), replacement.exists());

Copied: projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryHandlerUnitTestCase.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/test/java/org/jboss/test/virtual/test/ZipEntryHandlerUnitTestCase.java)
===================================================================
--- projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryHandlerUnitTestCase.java	                        (rev 0)
+++ projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryHandlerUnitTestCase.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,64 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.test.virtual.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.jboss.virtual.VFS;
+import org.jboss.virtual.VirtualFile;
+import org.jboss.virtual.plugins.context.jar.JarUtils;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContext;
+import org.jboss.virtual.spi.VFSContext;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.util.jar.*;
+
+/**
+ * ZipEntryHandlerUnitTestCase.
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+public class ZipEntryHandlerUnitTestCase extends JARVirtualFileHandlerUnitTestCase
+{
+   public ZipEntryHandlerUnitTestCase(String name)
+   {
+      super(name);
+   }
+
+   public static Test suite()
+   {
+      return new TestSuite(ZipEntryHandlerUnitTestCase.class);
+   }
+
+   protected VFSContext getVFSContext(String name) throws Exception
+   {
+      URL url = getRootResource(name);
+      url = JarUtils.createJarURL(url);
+      return new ZipEntryContext(url);
+   }
+
+}
\ No newline at end of file

Copied: projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryVFSContextUnitTestCase.java (from rev 72878, projects/vfs/branches/jar-alter-work/src/test/java/org/jboss/test/virtual/test/ZipEntryVFSContextUnitTestCase.java)
===================================================================
--- projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryVFSContextUnitTestCase.java	                        (rev 0)
+++ projects/vfs/trunk/src/test/java/org/jboss/test/virtual/test/ZipEntryVFSContextUnitTestCase.java	2008-06-01 18:25:02 UTC (rev 73901)
@@ -0,0 +1,141 @@
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2006, JBoss Inc., and individual contributors as indicated
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* This is free software; you can redistribute it and/or modify it
+* under the terms of the GNU Lesser General Public License as
+* published by the Free Software Foundation; either version 2.1 of
+* the License, or (at your option) any later version.
+*
+* This software is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this software; if not, write to the Free
+* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+*/
+package org.jboss.test.virtual.test;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.jboss.virtual.VFS;
+import org.jboss.virtual.plugins.context.jar.JarUtils;
+import org.jboss.virtual.plugins.context.zip.ZipEntryContext;
+import org.jboss.virtual.spi.VFSContext;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+/**
+ * ZipEntryVFSContextUnitTestCase.
+ *
+ * @author <a href="strukelj at parsek.net">Marko Strukelj</a>
+ * @version $Revision: 1.0 $
+ */
+public class ZipEntryVFSContextUnitTestCase extends JARVFSContextUnitTestCase
+{
+   public ZipEntryVFSContextUnitTestCase(String name)
+   {
+      super(name);
+   }
+
+   public static Test suite()
+   {
+      VFS.init();
+      System.out.println("java.protocol.handler.pkgs: " + System.getProperty("java.protocol.handler.pkgs"));
+      return new TestSuite(ZipEntryVFSContextUnitTestCase.class);
+   }
+
+   protected VFSContext getVFSContext(String name) throws Exception
+   {
+      URL url = getResource("/vfs/context/jar/" + name + ".jar");
+      url = JarUtils.createJarURL(url);
+      return new ZipEntryContext(url);
+   }
+
+
+
+   /**
+    * Analog to the same test in {@link JARVFSContextUnitTestCase}
+    *
+    * @throws Exception
+    */
+   public void testJarEntryAsRoot() throws Exception
+   {
+      URL url = getResource("/vfs/context/jar/simple.jar");
+      URL entry = new URL("jar:" + url.toString() + "!/child");
+      //entry.openStream().close();
+      ZipEntryContext context = new ZipEntryContext(entry);
+      assertEquals("child", context.getRoot().getName());
+
+      url = getResource("/vfs/test/outer.jar");
+      entry = new URL("jar:" + url.toString() + "!/jar2.jar ");
+      //entry.openStream().close();
+      context = new ZipEntryContext(entry);
+      assertEquals("jar2.jar", context.getRoot().getName());
+   }
+
+   /**
+    * Analog to the same test in {@link JARVFSContextUnitTestCase}
+    *
+    * @throws Exception
+    */
+   public void testPathIsEmptryForJarEntryAsRoot() throws Exception
+   {
+      URL url = getResource("/vfs/context/jar/simple.jar");
+      URL entry = new URL("jar:" + url.toString() + "!/child");
+      //entry.openStream().close();
+      ZipEntryContext context = new ZipEntryContext(entry);
+      assertEquals("child", context.getRoot().getName());
+      assertEquals("", context.getRoot().getPathName());
+
+      url = getResource("/vfs/test/outer.jar");
+      entry = new URL("jar:" + url.toString() + "!/jar2.jar ");
+      //entry.openStream().close();
+      context = new ZipEntryContext(entry);
+      assertEquals("jar2.jar", context.getRoot().getName());
+      assertEquals("", context.getRoot().getPathName());
+   }
+
+   /**
+    * Test detection of underlying jar file removal through exists()
+    *
+    * @throws Exception
+    */
+   public void testRootExists() throws Exception
+   {
+      URL url = getResource("/vfs/test/outer.jar");
+      File tmpJar = File.createTempFile("vfstest", ".jar");
+
+      InputStream is = url.openStream();
+      OutputStream os = new FileOutputStream(tmpJar);
+
+      byte [] buff = new byte[65536];
+      int count = is.read(buff);
+      while(count != -1)
+      {
+         os.write(buff, 0, count);
+         count = is.read(buff);
+      }
+      os.close();
+
+      // use noReaper so that the underlying file is not locked
+      // when we try to delete it
+      String jarUrl = tmpJar.toURL().toString() + "?noReaper=true";
+      ZipEntryContext context = new ZipEntryContext(new URL(jarUrl));
+      assertTrue("context.getRoot().exists()", context.getRoot().exists());
+
+      boolean isDeleted = tmpJar.delete();
+      assertTrue("delete tmp file: " + tmpJar, isDeleted);
+
+      assertFalse("context.getRoot().exists()", context.getRoot().exists());
+   }
+}
\ No newline at end of file




More information about the jboss-cvs-commits mailing list