[jboss-cvs] JBossAS SVN: r74575 - in projects/vfs/trunk/src/main/java/org/jboss/virtual: plugins/context/zip and 1 other directory.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Jun 15 10:50:45 EDT 2008


Author: mstruk
Date: 2008-06-15 10:50:44 -0400 (Sun, 15 Jun 2008)
New Revision: 74575

Modified:
   projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.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/ZipStreamWrapper.java
Log:
Implemented performance optimized in-memory nested archives, added jboss.vfs.optimizeForMemory system property for situations where lesser memory consumption wins over degraded performance

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-15 14:28:27 UTC (rev 74574)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/VFSUtils.java	2008-06-15 14:50:44 UTC (rev 74575)
@@ -90,6 +90,11 @@
    public static final String CASE_SENSITIVE_QUERY = "caseSensitive";
 
    /**
+    * Key used to turn on memory optimizations - less cache use at the expense of performance
+    */
+   public static final String OPTIMIZE_FOR_MEMORY_KEY = "jboss.vfs.optimizeForMemory";
+
+   /**
     * Get the paths string for a collection of virtual files
     *
     * @param paths the paths

Modified: 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/ZipEntryContext.java	2008-06-15 14:28:27 UTC (rev 74574)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipEntryContext.java	2008-06-15 14:50:44 UTC (rev 74575)
@@ -66,12 +66,16 @@
  *
  * 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.
+ * instances of <tt>ZipEntryContext</tt> with <tt>ZipStreamWrapper</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>.
  *
+ * In-memory nested archives may consume a lot of memory. To reduce memory footprint
+ * at the expense of performance, system property <em>jboss.vfs.optimizeForMemory=true<em>
+ * can be set.
+ *
  * 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

Modified: 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/ZipStreamWrapper.java	2008-06-15 14:28:27 UTC (rev 74574)
+++ projects/vfs/trunk/src/main/java/org/jboss/virtual/plugins/context/zip/ZipStreamWrapper.java	2008-06-15 14:50:44 UTC (rev 74575)
@@ -21,10 +21,19 @@
 */
 package org.jboss.virtual.plugins.context.zip;
 
+import org.jboss.virtual.VFSUtils;
+import org.jboss.logging.Logger;
+
 import java.io.*;
 import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import java.security.PrivilegedAction;
+import java.security.AccessController;
 
 /**
  * ZipStreamWrapper - for abstracted access to in-memory zip file
@@ -34,6 +43,26 @@
  */
 class ZipStreamWrapper extends ZipBytesWrapper
 {
+   /** Logger */
+   private static final Logger log = Logger.getLogger(ZipStreamWrapper.class);
+
+   /** Is optimizeForMemory turned on */
+   private static boolean optimizeForMemory;
+
+   static
+   {
+      optimizeForMemory = AccessController.doPrivileged(new CheckOptimizeForMemory());
+
+      if (optimizeForMemory)
+         log.info("VFS optimizeForMemory is enabled.");
+   }
+
+   /** zip archive - as individual inflated in-memory files */
+   private Map<String, InMemoryFile> inMemoryFiles = new LinkedHashMap<String, InMemoryFile>();
+
+   /** size of the zip file composed back from inMemoryFiles */
+   private int size;
+
    /**
     * ZipStreamWrapper is not aware of actual zip source so it can not detect
     * if it's been modified, like ZipFileWrapper does.
@@ -46,65 +75,157 @@
    ZipStreamWrapper(InputStream zipStream, String name, long lastModified) throws IOException
    {
       super(zipStream, name, lastModified);
+
+      ZipInputStream zis = new ZipInputStream(super.getRootAsStream());
+      ZipEntry ent = zis.getNextEntry();
+      while (ent != null)
+      {
+         byte [] fileBytes;
+         if (ent.isDirectory() == false)
+         {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            copyStream(zis, baos);
+            fileBytes = baos.toByteArray();
+            ent.setSize(fileBytes.length);
+         }
+         else
+         {
+            fileBytes = new byte[0];
+         }
+
+         inMemoryFiles.put(ent.getName(), new InMemoryFile(ent, fileBytes));
+         ent = zis.getNextEntry();
+      }
+
+      if (optimizeForMemory) {
+         initZipSize();
+
+         // we don't need memory buffer any more
+         super.close();
+      }
    }
 
    InputStream openStream(ZipEntry ent) throws IOException
    {
-      ZipInputStream zis = new ZipInputStream(getRootAsStream());
+      InMemoryFile memFile = inMemoryFiles.get(ent.getName());
 
-      // first find the entry
-      ZipEntry entry = zis.getNextEntry();
-      while(entry != null)
+      if (memFile == null)
+         throw new FileNotFoundException("Failed to find nested jar entry: " + ent.getName() + " in zip stream: " + toString());
+
+      return new ByteArrayInputStream(memFile.fileBytes);
+   }
+
+   Enumeration<? extends ZipEntry> entries() throws IOException
+   {
+      return new ZipStreamEnumeration();
+   }
+
+   InputStream getRootAsStream() throws FileNotFoundException
+   {
+      if (optimizeForMemory)
       {
-         if(entry.getName().equals(ent.getName()))
-            break;
-         entry = zis.getNextEntry();
+         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+         try
+         {
+            recomposeZip(baos);
+            return new ByteArrayInputStream(baos.toByteArray());
+         }
+         catch (IOException ex)
+         {
+            FileNotFoundException e = new FileNotFoundException("Failed to recompose inflated nested archive " + getName());
+            e.initCause(ex);
+            throw e;
+         }
       }
-      if(entry == null)
-         throw new IOException("Failed to find nested jar entry: " + ent.getName() + " in zip stream: " + toString());
+      else
+      {
+         return super.getRootAsStream();
+      }
+   }
 
-      // then read it
-      return new SizeLimitedInputStream(zis, ent.getSize());
+   long getSize()
+   {
+      if (optimizeForMemory)
+         return size;
+      else
+         return super.getSize();
    }
 
-   Enumeration<? extends ZipEntry> entries() throws IOException
+   void close() {
+      inMemoryFiles = null;
+      super.close();
+   }
+
+   private void initZipSize() throws IOException {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      recomposeZip(baos);
+      this.size = baos.size();
+   }
+
+   private void recomposeZip(ByteArrayOutputStream baos) throws IOException
    {
-      return new ZipStreamEnumeration(new ZipInputStream(getRootAsStream()));
+      ZipOutputStream zout = new ZipOutputStream(baos);
+      zout.setMethod(ZipOutputStream.STORED);
+
+      Iterator<InMemoryFile> it = inMemoryFiles.values().iterator();
+      while(it.hasNext())
+      {
+         InMemoryFile memFile = it.next();
+         zout.putNextEntry(memFile.entry);
+         zout.write(memFile.fileBytes);
+      }
+      zout.close();
    }
 
-   /**
-    * Zip stream enumeration.
-    */
-   class ZipStreamEnumeration implements Enumeration<ZipEntry>
+   private static void copyStream(InputStream is, OutputStream os) throws IOException
    {
-      private ZipInputStream zis;
+      byte [] buff = new byte[4096];
+      int rc = is.read(buff);
+      while (rc != -1)
+      {
+         os.write(buff, 0, rc);
+         rc = is.read(buff);
+      }
+   }
 
-      private ZipEntry entry;
+   static class InMemoryFile
+   {
+      ZipEntry entry;
+      byte [] fileBytes;
 
-      ZipStreamEnumeration(ZipInputStream zis) throws IOException
+      public InMemoryFile(ZipEntry entry, byte[] fileBytes)
       {
-         this.zis = zis;
-         entry = zis.getNextEntry();
+         this.entry = entry;
+         this.fileBytes = fileBytes;
       }
+   }
 
+   class ZipStreamEnumeration implements Enumeration<ZipEntry>
+   {
+      private Iterator<InMemoryFile> it;
+
+      ZipStreamEnumeration()
+      {
+         it = inMemoryFiles.values().iterator();
+      }
+
       public boolean hasMoreElements()
       {
-         return entry != null;
+         return it.hasNext();
       }
 
       public ZipEntry nextElement()
       {
-         ZipEntry ret = entry;
-         try
-         {
-            entry = zis.getNextEntry();
-         }
-         catch (IOException ex)
-         {
-            throw new RuntimeException("Failed to retrieve next entry from zip stream", ex);
-         }
+         return it.next().entry;
+      }
+   }
 
-         return ret;
+   private static class CheckOptimizeForMemory implements PrivilegedAction<Boolean>
+   {
+      public Boolean run()
+      {
+         String forceString = System.getProperty(VFSUtils.OPTIMIZE_FOR_MEMORY_KEY, "false");
+         return Boolean.valueOf(forceString);
       }
    }
 }




More information about the jboss-cvs-commits mailing list