Author: pauls
Date: Wed Mar 14 13:28:58 2018
New Revision: 1826714

URL: http://svn.apache.org/viewvc?rev=1826714&view=rev
Log:
FELIX-5808: Clean-up bundlecache manifest parsing and entry reading

Modified:
    
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
    
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
    
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
    
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
    
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java

Modified: 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java?rev=1826714&r1=1826713&r2=1826714&view=diff
==============================================================================
--- 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
 (original)
+++ 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
 Wed Mar 14 13:28:58 2018
@@ -18,16 +18,23 @@
  */
 package org.apache.felix.framework.cache;
 
-import java.io.*;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.util.*;
-
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.SecureAction;
 import org.apache.felix.framework.util.WeakZipFileFactory;
 import org.osgi.framework.Constants;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
 /**
  * <p>
  * This class, combined with <tt>BundleArchive</tt>, and concrete
@@ -83,8 +90,8 @@ public class BundleCache
     // TODO: CACHE - This should eventually be removed along with the code
     //       supporting the old multi-file bundle cache format.
     public static final String CACHE_SINGLEBUNDLEFILE_PROP = 
"felix.cache.singlebundlefile";
-
-    protected static transient int BUFSIZE = 4096;
+    private static final ThreadLocal m_defaultBuffer = new ThreadLocal();
+    private static volatile int DEFAULT_BUFFER = 1024 * 64;
 
     private static transient final String CACHE_DIR_NAME = "felix-cache";
     private static transient final String CACHE_ROOTDIR_DEFAULT = ".";
@@ -182,6 +189,187 @@ public class BundleCache
         }
     }
 
+    // Parse the main attributes of the manifest of the given jarfile.
+    // The idea is to not open the jar file as a java.util.jarfile but
+    // read the mainfest from the zipfile directly and parse it manually
+    // to use less memory and be faster.
+    //
+    // @return the given map for convenience
+    public static Map<String, Object> getMainAttributes(Map<String, Object> 
headers, InputStream inputStream, long size) throws Exception
+    {
+        if (size > 0)
+        {
+            return getMainAttributes(headers, inputStream, (int) (size < 
Integer.MAX_VALUE ? size : Integer.MAX_VALUE));
+        }
+        else
+        {
+            return headers;
+        }
+    }
+
+    static byte[] read(InputStream input, long size) throws Exception
+    {
+        return read(input, size <= Integer.MAX_VALUE ? (int) size : 
Integer.MAX_VALUE);
+    }
+
+    static byte[] read(InputStream input, int size) throws Exception
+    {
+        if (size <= 0)
+        {
+            return new byte[0];
+        }
+
+        byte[] result = new byte[size];
+
+        Exception exception = null;
+        try
+        {
+            for (int i = input.read(result, 0, size); i != -1 && i < size; i 
+= input.read(result, i, size - i))
+            {
+
+            }
+        }
+        catch (Exception ex)
+        {
+            exception = ex;
+        }
+        finally
+        {
+            try
+            {
+                input.close();
+            }
+            catch (Exception ex)
+            {
+                throw exception != null ? exception : ex;
+            }
+        }
+
+        return result;
+    }
+
+    public static Map<String, Object> getMainAttributes(Map<String, Object> 
headers, InputStream inputStream, int size) throws Exception
+    {
+        if (size <= 0)
+        {
+            inputStream.close();
+            return headers;
+        }
+
+        // Get the buffer for this thread if there is one already otherwise,
+        // create one of size DEFAULT_BUFFER (64K) if the manifest is less
+        // than 64k or of the size of the manifest.
+        SoftReference ref = (SoftReference) m_defaultBuffer.get();
+        byte[] bytes = null;
+        if (ref != null)
+        {
+            bytes = (byte[]) ref.get();
+        }
+
+        if (bytes == null)
+        {
+            bytes = new byte[size + 1 > DEFAULT_BUFFER ? size + 1 : 
DEFAULT_BUFFER];
+            m_defaultBuffer.set(new SoftReference(bytes));
+        }
+        else if (size + 1 > bytes.length)
+        {
+            bytes = new byte[size + 1];
+            m_defaultBuffer.set(new SoftReference(bytes));
+        }
+
+        // Now read in the manifest in one go into the bytes array.
+        // The InputStream should be already buffered and can handle up to 64K 
buffers in one go.
+        try
+        {
+            int i = inputStream.read(bytes);
+            while (i < size)
+            {
+                i += inputStream.read(bytes, i, bytes.length - i);
+            }
+        }
+        finally
+        {
+            inputStream.close();
+        }
+
+        // Force a new line at the end of the manifest to deal with broken 
manifest without any line-ending
+        bytes[size++] = '\n';
+
+        // Now parse the main attributes. The idea is to do that
+        // without creating new byte arrays. Therefore, we read through
+        // the manifest bytes inside the bytes array and write them back into
+        // the same array unless we don't need them (e.g., \r\n and \n are 
skipped).
+        // That allows us to create the strings from the bytes array without 
the skipped
+        // chars. We stop as soon as we see a blank line as that denotes that 
the main
+        // attributes part is finished.
+        String key = null;
+        int last = 0;
+        int current = 0;
+        for (int i = 0; i < size; i++)
+        {
+            // skip \r and \n if it is followed by another \n
+            // (we catch the blank line case in the next iteration)
+            if (bytes[i] == '\r')
+            {
+                if ((i + 1 < size) && (bytes[i + 1] == '\n'))
+                {
+                    continue;
+                }
+            }
+            if (bytes[i] == '\n')
+            {
+                if ((i + 1 < size) && (bytes[i + 1] == ' '))
+                {
+                    i++;
+                    continue;
+                }
+            }
+            // If we don't have a key yet and see the first : we parse it as 
the key
+            // and skip the :<blank> that follows it.
+            if ((key == null) && (bytes[i] == ':'))
+            {
+                key = new String(bytes, last, (current - last), "UTF-8");
+                if ((i + 1 < size) && (bytes[i + 1] == ' '))
+                {
+                    last = current + 1;
+                    continue;
+                }
+                else
+                {
+                    throw new Exception("Manifest error: Missing space 
separator - " + key);
+                }
+            }
+            // if we are at the end of a line
+            if (bytes[i] == '\n')
+            {
+                // and it is a blank line stop parsing (main attributes are 
done)
+                if ((last == current) && (key == null))
+                {
+                    break;
+                }
+                // Otherwise, parse the value and add it to the map (we throw 
an
+                // exception if we don't have a key or the key already exist.
+                String value = new String(bytes, last, (current - last), 
"UTF-8");
+                if (key == null)
+                {
+                    throw new Exception("Manifest error: Missing attribute 
name - " + value);
+                }
+                else if (headers.put(key.intern(), value) != null)
+                {
+                    throw new Exception("Manifest error: Duplicate attribute 
name - " + key);
+                }
+                last = current;
+                key = null;
+            }
+            else
+            {
+                // write back the byte if it needs to be included in the key 
or the value.
+                bytes[current++] = bytes[i];
+            }
+        }
+        return headers;
+    }
+
     public synchronized void release()
     {
         if (m_lock != null)
@@ -222,7 +410,7 @@ public class BundleCache
             String sBufSize = (String) m_configMap.get(CACHE_BUFSIZE_PROP);
             if (sBufSize != null)
             {
-                BUFSIZE = Integer.parseInt(sBufSize);
+                DEFAULT_BUFFER = Integer.parseInt(sBufSize);
             }
         }
         catch (NumberFormatException ne)
@@ -346,23 +534,41 @@ public class BundleCache
     static void copyStreamToFile(InputStream is, File outputFile)
         throws IOException
     {
+        // Get the buffer for this thread if there is one already otherwise,
+        // create one of size DEFAULT_BUFFER
+        SoftReference ref = (SoftReference) m_defaultBuffer.get();
+        byte[] bytes = null;
+        if (ref != null)
+        {
+            bytes = (byte[]) ref.get();
+        }
+
+        if (bytes == null)
+        {
+            bytes = new byte[DEFAULT_BUFFER];
+            m_defaultBuffer.set(new SoftReference(bytes));
+        }
+
         OutputStream os = null;
 
         try
         {
             os = getSecureAction().getFileOutputStream(outputFile);
-            os = new BufferedOutputStream(os, BUFSIZE);
-            byte[] b = new byte[BUFSIZE];
-            int len = 0;
-            while ((len = is.read(b)) != -1)
+            for (int i = is.read(bytes);i != -1; i = is.read(bytes))
             {
-                os.write(b, 0, len);
+                os.write(bytes, 0, i);
             }
         }
         finally
         {
-            if (is != null) is.close();
-            if (os != null) os.close();
+            try
+            {
+                if (is != null) is.close();
+            }
+            finally
+            {
+                if (os != null) os.close();
+            }
         }
     }
 

Modified: 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java?rev=1826714&r1=1826713&r2=1826714&view=diff
==============================================================================
--- 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
 (original)
+++ 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
 Wed Mar 14 13:28:58 2018
@@ -18,16 +18,23 @@
  */
 package org.apache.felix.framework.cache;
 
-import java.io.*;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.*;
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.FelixConstants;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.WeakZipFileFactory;
 import org.osgi.framework.Constants;
 
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
 public class DirectoryContent implements Content
 {
     private static final int BUFSIZE = 4096;
@@ -96,44 +103,20 @@ public class DirectoryContent implements
         }
 
         // Get the embedded resource.
-        InputStream is = null;
-        ByteArrayOutputStream baos = null;
 
+        File file = new File(m_dir, name);
         try
         {
-            is = new BufferedInputStream(
-                BundleCache.getSecureAction().getFileInputStream(new 
File(m_dir, name)));
-            baos = new ByteArrayOutputStream(BUFSIZE);
-            byte[] buf = new byte[BUFSIZE];
-            int n = 0;
-            while ((n = is.read(buf, 0, buf.length)) >= 0)
-            {
-                baos.write(buf, 0, n);
-            }
-            return baos.toByteArray();
 
+            return 
BundleCache.read(BundleCache.getSecureAction().getFileInputStream(file), 
file.length());
         }
         catch (Exception ex)
         {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "DirectoryContent: Unable to read bytes for file " + name + " 
from file " + file.getAbsolutePath(), ex);
             return null;
         }
-        finally
-        {
-            try
-            {
-                if (baos != null) baos.close();
-            }
-            catch (Exception ex)
-            {
-            }
-            try
-            {
-                if (is != null) is.close();
-            }
-            catch (Exception ex)
-            {
-            }
-        }
     }
 
     public InputStream getEntryAsStream(String name)
@@ -144,7 +127,18 @@ public class DirectoryContent implements
             name = name.substring(1);
         }
 
-        return BundleCache.getSecureAction().getFileInputStream(new 
File(m_dir, name));
+        File file = new File(m_dir, name);
+        try
+        {
+            return BundleCache.getSecureAction().getFileInputStream(file);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(
+                Logger.LOG_ERROR,
+                "DirectoryContent: Unable to create inputstream for file " + 
name + " from file " + file.getAbsolutePath(), ex);
+            return null;
+        }
     }
 
     public URL getEntryAsURL(String name)
@@ -272,13 +266,7 @@ public class DirectoryContent implements
 
                         try
                         {
-                            is = new BufferedInputStream(
-                                
BundleCache.getSecureAction().getFileInputStream(entryFile),
-                                BundleCache.BUFSIZE);
-                            if (is == null)
-                            {
-                                throw new IOException("No input stream: " + 
entryName);
-                            }
+                            is = 
BundleCache.getSecureAction().getFileInputStream(entryFile);
 
                             // Create the file.
                             BundleCache.copyStreamToFile(is, libFile);
@@ -305,17 +293,6 @@ public class DirectoryContent implements
                                 Logger.LOG_ERROR,
                                 "Extracting native library.", ex);
                         }
-                        finally
-                        {
-                            try
-                            {
-                                if (is != null) is.close();
-                            }
-                            catch (IOException ex)
-                            {
-                                // Not much we can do.
-                            }
-                        }
                     }
                 }
                 else

Modified: 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java?rev=1826714&r1=1826713&r2=1826714&view=diff
==============================================================================
--- 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
 (original)
+++ 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
 Wed Mar 14 13:28:58 2018
@@ -18,16 +18,14 @@
  */
 package org.apache.felix.framework.cache;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-import java.util.jar.Manifest;
-
 import org.apache.felix.framework.Logger;
 import org.apache.felix.framework.util.StringMap;
 import org.apache.felix.framework.util.WeakZipFileFactory;
 
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
 /**
  * <p>
  * This class implements a bundle archive revision for exploded bundle
@@ -72,29 +70,8 @@ class DirectoryRevision extends BundleAr
     public synchronized Map<String, Object> getManifestHeader()
         throws Exception
     {
-        // Read the header file from the reference directory.
-        InputStream is = null;
-
-        try
-        {
-            // Open manifest file.
-            is = BundleCache.getSecureAction()
-                .getFileInputStream(new File(m_refDir, 
"META-INF/MANIFEST.MF"));
-            // Error if no jar file.
-            if (is == null)
-            {
-                throw new IOException("No manifest file found.");
-            }
-
-            // Get manifest.
-            Manifest mf = new Manifest(is);
-            // Create a case insensitive map of manifest attributes.
-            return new StringMap(mf.getMainAttributes());
-        }
-        finally
-        {
-            if (is != null) is.close();
-        }
+        File manifest = new File(m_refDir, "META-INF/MANIFEST.MF");
+        return manifest.isFile() ? BundleCache.getMainAttributes(new 
StringMap(), BundleCache.getSecureAction().getFileInputStream(manifest), 
manifest.length()) : null;
     }
 
     public synchronized Content getContent() throws Exception

Modified: 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java?rev=1826714&r1=1826713&r2=1826714&view=diff
==============================================================================
--- 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
 (original)
+++ 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
 Wed Mar 14 13:28:58 2018
@@ -18,8 +18,13 @@
  */
 package org.apache.felix.framework.cache;
 
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
+import org.osgi.framework.Constants;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -30,16 +35,9 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
 import java.util.zip.ZipEntry;
-import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.WeakZipFileFactory;
-import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
-import org.osgi.framework.Constants;
 
 public class JarContent implements Content
 {
-    private static final int BUFSIZE = 4096;
     private static final transient String EMBEDDED_DIRECTORY = "-embedded";
     private static final transient String LIBRARY_DIRECTORY = "-lib";
 
@@ -131,9 +129,6 @@ public class JarContent implements Conte
     public byte[] getEntryAsBytes(String name) throws IllegalStateException
     {
         // Get the embedded resource.
-        InputStream is = null;
-        ByteArrayOutputStream baos = null;
-
         try
         {
             ZipEntry ze = m_zipFile.getEntry(name);
@@ -141,19 +136,8 @@ public class JarContent implements Conte
             {
                 return null;
             }
-            is = m_zipFile.getInputStream(ze);
-            if (is == null)
-            {
-                return null;
-            }
-            baos = new ByteArrayOutputStream(BUFSIZE);
-            byte[] buf = new byte[BUFSIZE];
-            int n = 0;
-            while ((n = is.read(buf, 0, buf.length)) >= 0)
-            {
-                baos.write(buf, 0, n);
-            }
-            return baos.toByteArray();
+
+            return BundleCache.read(m_zipFile.getInputStream(ze), 
ze.getSize());
 
         }
         catch (Exception ex)
@@ -163,23 +147,6 @@ public class JarContent implements Conte
                 "JarContent: Unable to read bytes for file " + name + " in ZIP 
file " + m_file.getAbsolutePath(), ex);
             return null;
         }
-        finally
-        {
-            try
-            {
-                if (baos != null) baos.close();
-            }
-            catch (Exception ex)
-            {
-            }
-            try
-            {
-                if (is != null) is.close();
-            }
-            catch (Exception ex)
-            {
-            }
-        }
     }
 
     public InputStream getEntryAsStream(String name)
@@ -348,20 +315,10 @@ public class JarContent implements Conte
                     }
                     else
                     {
-                        InputStream is = null;
-
                         try
                         {
-                            is = new BufferedInputStream(
-                                m_zipFile.getInputStream(ze),
-                                BundleCache.BUFSIZE);
-                            if (is == null)
-                            {
-                                throw new IOException("No input stream: " + 
entryName);
-                            }
-
                             // Create the file.
-                            BundleCache.copyStreamToFile(is, libFile);
+                            
BundleCache.copyStreamToFile(m_zipFile.getInputStream(ze), libFile);
 
                             // Perform exec permission command on extracted 
library
                             // if one is configured.
@@ -398,17 +355,6 @@ public class JarContent implements Conte
                                 Logger.LOG_ERROR,
                                 "Extracting native library.", ex);
                         }
-                        finally
-                        {
-                            try
-                            {
-                                if (is != null) is.close();
-                            }
-                            catch (IOException ex)
-                            {
-                                // Not much we can do.
-                            }
-                        }
                     }
                 }
                 else
@@ -435,7 +381,6 @@ public class JarContent implements Conte
     /**
      * This method extracts an embedded JAR file from the bundle's
      * JAR file.
-     * @param id the identifier of the bundle that owns the embedded JAR file.
      * @param jarPath the path to the embedded JAR file inside the bundle JAR 
file.
     **/
     private void extractEmbeddedJar(String jarPath)
@@ -453,44 +398,30 @@ public class JarContent implements Conte
 
         if (!BundleCache.getSecureAction().fileExists(jarFile))
         {
-            InputStream is = null;
-            try
+            // Make sure class path entry is a JAR file.
+            ZipEntry ze = m_zipFile.getEntry(jarPath);
+            if (ze == null)
             {
-                // Make sure class path entry is a JAR file.
-                ZipEntry ze = m_zipFile.getEntry(jarPath);
-                if (ze == null)
-                {
-                    return;
-                }
-                // If the zip entry is a directory, then ignore it since
-                // we don't need to extact it; otherwise, it points to an
-                // embedded JAR file, so extract it.
-                else if (!ze.isDirectory())
+                return;
+            }
+            // If the zip entry is a directory, then ignore it since
+            // we don't need to extact it; otherwise, it points to an
+            // embedded JAR file, so extract it.
+            else if (!ze.isDirectory())
+            {
+                // Make sure that the embedded JAR's parent directory exists;
+                // it may be in a sub-directory.
+                File jarDir = jarFile.getParentFile();
+                if (!BundleCache.getSecureAction().fileExists(jarDir))
                 {
-                    // Make sure that the embedded JAR's parent directory 
exists;
-                    // it may be in a sub-directory.
-                    File jarDir = jarFile.getParentFile();
-                    if (!BundleCache.getSecureAction().fileExists(jarDir))
-                    {
-                        if (!BundleCache.getSecureAction().mkdirs(jarDir))
-                        {
-                            throw new IOException("Unable to create embedded 
JAR directory.");
-                        }
-                    }
-
-                    // Extract embedded JAR into its directory.
-                    is = new BufferedInputStream(m_zipFile.getInputStream(ze), 
BundleCache.BUFSIZE);
-                    if (is == null)
+                    if (!BundleCache.getSecureAction().mkdirs(jarDir))
                     {
-                        throw new IOException("No input stream: " + jarPath);
+                        throw new IOException("Unable to create embedded JAR 
directory.");
                     }
-                    // Copy the file.
-                    BundleCache.copyStreamToFile(is, jarFile);
                 }
-            }
-            finally
-            {
-                if (is != null) is.close();
+
+                // Extract embedded JAR into its directory.
+                BundleCache.copyStreamToFile(m_zipFile.getInputStream(ze), 
jarFile);
             }
         }
     }

Modified: 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java?rev=1826714&r1=1826713&r2=1826714&view=diff
==============================================================================
--- 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
 (original)
+++ 
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
 Wed Mar 14 13:28:58 2018
@@ -18,22 +18,21 @@
  */
 package org.apache.felix.framework.cache;
 
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.WeakZipFileFactory;
+import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ref.SoftReference;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.Map;
 import java.util.zip.ZipEntry;
 
-import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.StringMap;
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.WeakZipFileFactory;
-import org.apache.felix.framework.util.WeakZipFileFactory.WeakZipFile;
-
 /**
  * <p>
  * This class implements a bundle archive revision for a standard bundle
@@ -98,11 +97,12 @@ class JarRevision extends BundleArchiveR
 
     public Map<String, Object> getManifestHeader() throws Exception
     {
-        // Create a case insensitive map of manifest attributes.
-        Map<String, Object> headers = new StringMap();
-        // Read and parse headers.
-        getMainAttributes(headers, m_zipFile);
-        return headers;
+        // Read and parse headers into a case insensitive map of manifest 
attributes and return it.
+        ZipEntry manifestEntry = m_zipFile.getEntry("META-INF/MANIFEST.MF");
+
+        Map<String, Object> manifest = manifestEntry != null ? 
BundleCache.getMainAttributes(new StringMap(), 
m_zipFile.getInputStream(manifestEntry), manifestEntry.getSize()) : null;
+
+        return manifest;
     }
 
     public synchronized Content getContent() throws Exception
@@ -186,129 +186,4 @@ class JarRevision extends BundleArchiveR
             if (is != null) is.close();
         }
     }
-
-    private static final ThreadLocal m_defaultBuffer = new ThreadLocal();
-    private static final int DEFAULT_BUFFER = 1024 * 64;
-
-    // Parse the main attributes of the manifest of the given jarfile.
-    // The idea is to not open the jar file as a java.util.jarfile but
-    // read the mainfest from the zipfile directly and parse it manually
-    // to use less memory and be faster.
-    private static void getMainAttributes(Map<String, Object> result, 
WeakZipFile zipFile) throws Exception
-    {
-        ZipEntry entry = zipFile.getEntry("META-INF/MANIFEST.MF");
-
-        // Get a buffer for this thread if there is one already otherwise,
-        // create one of size DEFAULT_BUFFER (64K) if the manifest is less
-        // than 64k or of the size of the manifest.
-        SoftReference ref = (SoftReference) m_defaultBuffer.get();
-        byte[] bytes = null;
-        if (ref != null)
-        {
-            bytes = (byte[]) ref.get();
-        }
-        int size = (int) entry.getSize();
-        if (bytes == null)
-        {
-            bytes = new byte[size > DEFAULT_BUFFER ? size : DEFAULT_BUFFER];
-            m_defaultBuffer.set(new SoftReference(bytes));
-        }
-        else if (size > bytes.length)
-        {
-            bytes = new byte[size];
-            m_defaultBuffer.set(new SoftReference(bytes));
-        }
-
-        // Now read in the manifest in one go into the bytes array.
-        // The InputStream is already
-        // buffered and can handle up to 64K buffers in one go.
-        InputStream is = null;
-        try
-        {
-            is = zipFile.getInputStream(entry);
-            int i = is.read(bytes);
-            while (i < size)
-            {
-                i += is.read(bytes, i, bytes.length - i);
-            }
-        }
-        finally
-        {
-            is.close();
-        }
-
-        // Now parse the main attributes. The idea is to do that
-        // without creating new byte arrays. Therefore, we read through
-        // the manifest bytes inside the bytes array and write them back into
-        // the same array unless we don't need them (e.g., \r\n and \n are 
skipped).
-        // That allows us to create the strings from the bytes array without 
the skipped
-        // chars. We stopp as soon as we see a blankline as that denotes that 
the main
-        //attributes part is finished.
-        String key = null;
-        int last = 0;
-        int current = 0;
-        for (int i = 0; i < size; i++)
-        {
-            // skip \r and \n if it is follows by another \n
-            // (we catch the blank line case in the next iteration)
-            if (bytes[i] == '\r')
-            {
-                if ((i + 1 < size) && (bytes[i + 1] == '\n'))
-                {
-                    continue;
-                }
-            }
-            if (bytes[i] == '\n')
-            {
-                if ((i + 1 < size) && (bytes[i + 1] == ' '))
-                {
-                    i++;
-                    continue;
-                }
-            }
-            // If we don't have a key yet and see the first : we parse it as 
the key
-            // and skip the :<blank> that follows it.
-            if ((key == null) && (bytes[i] == ':'))
-            {
-                key = new String(bytes, last, (current - last), "UTF-8");
-                if ((i + 1 < size) && (bytes[i + 1] == ' '))
-                {
-                    last = current + 1;
-                    continue;
-                }
-                else
-                {
-                    throw new Exception(
-                        "Manifest error: Missing space separator - " + key);
-                }
-            }
-            // if we are at the end of a line
-            if (bytes[i] == '\n')
-            {
-                // and it is a blank line stop parsing (main attributes are 
done)
-                if ((last == current) && (key == null))
-                {
-                    break;
-                }
-                // Otherwise, parse the value and add it to the map (we throw 
an
-                // exception if we don't have a key or the key already exist.
-                String value = new String(bytes, last, (current - last), 
"UTF-8");
-                if (key == null)
-                {
-                    throw new Exception("Manifst error: Missing attribute name 
- " + value);
-                }
-                else if (result.put(key, value) != null)
-                {
-                    throw new Exception("Manifst error: Duplicate attribute 
name - " + key);
-                }
-                last = current;
-                key = null;
-            }
-            else
-            {
-                // write back the byte if it needs to be included in the key 
or the value.
-                bytes[current++] = bytes[i];
-            }
-        }
-    }
 }
\ No newline at end of file


Reply via email to