Implement custom manifest parsing to avoid using JarFiles, since they
appear to consume a lot of memory. (FELIX-2721)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1052040 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
index 5085fd6..3b5512a 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -32,7 +32,7 @@
import java.util.zip.ZipEntry;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.JarFileX;
+import org.apache.felix.framework.util.ZipFileX;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.resolver.Content;
import org.osgi.framework.Constants;
@@ -48,20 +48,20 @@
private final Object m_revisionLock;
private final File m_rootDir;
private final File m_file;
- private final JarFileX m_jarFile;
- private final boolean m_isJarFileOwner;
+ private final ZipFileX m_zipFile;
+ private final boolean m_isZipFileOwner;
private Map m_nativeLibMap;
public JarContent(Logger logger, Map configMap, Object revisionLock, File rootDir,
- File file, JarFileX jarFile)
+ File file, ZipFileX zipFile)
{
m_logger = logger;
m_configMap = configMap;
m_revisionLock = revisionLock;
m_rootDir = rootDir;
m_file = file;
- m_jarFile = (jarFile == null) ? openJarFile(m_file) : jarFile;
- m_isJarFileOwner = (jarFile == null);
+ m_zipFile = (zipFile == null) ? openZipFile(m_file) : zipFile;
+ m_isZipFileOwner = (zipFile == null);
}
protected void finalize()
@@ -73,9 +73,9 @@
{
try
{
- if (m_isJarFileOwner)
+ if (m_isZipFileOwner)
{
- m_jarFile.close();
+ m_zipFile.close();
}
}
catch (Exception ex)
@@ -90,7 +90,7 @@
{
try
{
- ZipEntry ze = m_jarFile.getEntry(name);
+ ZipEntry ze = m_zipFile.getEntry(name);
return ze != null;
}
catch (Exception ex)
@@ -105,7 +105,7 @@
public Enumeration getEntries()
{
// Wrap entries enumeration to filter non-matching entries.
- Enumeration e = new EntriesEnumeration(m_jarFile.entries());
+ Enumeration e = new EntriesEnumeration(m_zipFile.entries());
// Spec says to return null if there are no entries.
return (e.hasMoreElements()) ? e : null;
@@ -119,12 +119,12 @@
try
{
- ZipEntry ze = m_jarFile.getEntry(name);
+ ZipEntry ze = m_zipFile.getEntry(name);
if (ze == null)
{
return null;
}
- is = m_jarFile.getInputStream(ze);
+ is = m_zipFile.getInputStream(ze);
if (is == null)
{
return null;
@@ -173,12 +173,12 @@
try
{
- ZipEntry ze = m_jarFile.getEntry(name);
+ ZipEntry ze = m_zipFile.getEntry(name);
if (ze == null)
{
return null;
}
- is = m_jarFile.getInputStream(ze);
+ is = m_zipFile.getInputStream(ze);
if (is == null)
{
return null;
@@ -211,7 +211,7 @@
if (entryName.equals(FelixConstants.CLASS_PATH_DOT))
{
return new JarContent(m_logger, m_configMap, m_revisionLock,
- m_rootDir, m_file, m_jarFile);
+ m_rootDir, m_file, m_zipFile);
}
// Remove any leading slash.
@@ -228,7 +228,7 @@
// Determine if the entry is an emdedded JAR file or
// directory in the bundle JAR file. Ignore any entries
// that do not exist per the spec.
- ZipEntry ze = m_jarFile.getEntry(entryName);
+ ZipEntry ze = m_zipFile.getEntry(entryName);
if ((ze != null) && ze.isDirectory())
{
File extractDir = new File(embedDir, entryName);
@@ -297,7 +297,7 @@
// The entry name must refer to a file type, since it is
// a native library, not a directory.
- ZipEntry ze = m_jarFile.getEntry(entryName);
+ ZipEntry ze = m_zipFile.getEntry(entryName);
if ((ze != null) && !ze.isDirectory())
{
// Extracting the embedded native library file impacts all other
@@ -336,7 +336,7 @@
try
{
is = new BufferedInputStream(
- m_jarFile.getInputStream(ze),
+ m_zipFile.getInputStream(ze),
BundleCache.BUFSIZE);
if (is == null)
{
@@ -440,7 +440,7 @@
try
{
// Make sure class path entry is a JAR file.
- ZipEntry ze = m_jarFile.getEntry(jarPath);
+ ZipEntry ze = m_zipFile.getEntry(jarPath);
if (ze == null)
{
return;
@@ -462,7 +462,7 @@
}
// Extract embedded JAR into its directory.
- is = new BufferedInputStream(m_jarFile.getInputStream(ze), BundleCache.BUFSIZE);
+ is = new BufferedInputStream(m_zipFile.getInputStream(ze), BundleCache.BUFSIZE);
if (is == null)
{
throw new IOException("No input stream: " + jarPath);
@@ -478,11 +478,11 @@
}
}
- private static JarFileX openJarFile(File file) throws RuntimeException
+ private static ZipFileX openZipFile(File file) throws RuntimeException
{
try
{
- return BundleCache.getSecureAction().openJAR(file, false);
+ return BundleCache.getSecureAction().openZipFile(file);
}
catch (IOException ex)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
index 9e69f6f..ccd0109 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarRevision.java
@@ -18,6 +18,7 @@
*/
package org.apache.felix.framework.cache;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -26,10 +27,11 @@
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
-import java.util.jar.Manifest;
+import java.util.Map.Entry;
+import java.util.zip.ZipEntry;
import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.JarFileX;
+import org.apache.felix.framework.util.ZipFileX;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.resolver.Content;
@@ -50,7 +52,7 @@
private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
private File m_bundleFile = null;
- private final JarFileX m_jarFile;
+ private final ZipFileX m_zipFile;
public JarRevision(
Logger logger, Map configMap, File revisionRootDir,
@@ -82,21 +84,21 @@
initialize(byReference, is);
// Open shared copy of the JAR file.
- JarFileX jarFile = null;
+ ZipFileX zipFile = null;
try
{
// Open bundle JAR file.
- jarFile = BundleCache.getSecureAction().openJAR(m_bundleFile, false);
+ zipFile = BundleCache.getSecureAction().openZipFile(m_bundleFile);
// Error if no jar file.
- if (jarFile == null)
+ if (zipFile == null)
{
throw new IOException("No JAR file found.");
}
- m_jarFile = jarFile;
+ m_zipFile = zipFile;
}
catch (Exception ex)
{
- if (jarFile != null) jarFile.close();
+ if (zipFile != null) zipFile.close();
throw ex;
}
}
@@ -104,9 +106,9 @@
public Map getManifestHeader() throws Exception
{
// Get the embedded resource.
- Manifest mf = m_jarFile.getManifest();
+ Map headers = getMainAttributes(m_zipFile);
// Use an empty map if there is no manifest.
- Map headers = (mf == null) ? new HashMap() : mf.getMainAttributes();
+ headers = (headers == null) ? new HashMap() : headers;
// Create a case insensitive map of manifest attributes.
return new StringMap(headers, false);
}
@@ -114,12 +116,12 @@
public synchronized Content getContent() throws Exception
{
return new JarContent(getLogger(), getConfig(), this, getRevisionRootDir(),
- m_bundleFile, m_jarFile);
+ m_bundleFile, m_zipFile);
}
protected void close() throws Exception
{
- m_jarFile.close();
+ m_zipFile.close();
}
//
@@ -192,4 +194,134 @@
if (is != null) is.close();
}
}
+
+ public static int readLine(InputStream is, byte[] buf) throws IOException
+ {
+ for (int i = 0; i < buf.length; i++)
+ {
+ int b = is.read();
+ if (b < 0)
+ {
+ return (i == 0) ? -1 : i;
+ }
+ else
+ {
+ buf[i] = (byte) b;
+ if (buf[i] == '\n')
+ {
+ return i + 1;
+ }
+ }
+ }
+ return 0;
+ }
+
+ private static Map<String, String> getMainAttributes(ZipFileX zipFile) throws IOException
+ {
+ Map<String, String> mainAttrs = new HashMap<String, String>();
+ Map<String, byte[]> tmpMap = new HashMap<String, byte[]>();
+
+ byte[] buf = new byte[512];
+ ZipEntry ze = zipFile.getEntry("META-INF/MANIFEST.MF");
+ InputStream is = new BufferedInputStream(zipFile.getInputStream(ze));
+ String lastName = null;
+ try
+ {
+ for (int len = readLine(is, buf); len != -1; len = readLine(is, buf))
+ {
+ // Make sure line ends with a line feed.
+ if (buf[len - 1] != '\n')
+ {
+ throw new IOException(
+ "Manifest error: Line either too long or no line feed - "
+ + new String(buf, 0, 0, len));
+ }
+
+ // Ignore line feed.
+ len--;
+
+ // If line ends with carriage return, ignore it.
+ if ((len > 0) && (buf[len - 1] == '\r'))
+ {
+ len--;
+ }
+
+ // If line is empty, then we've reached the end
+ // of the main attributes group.
+ if (len == 0)
+ {
+ break;
+ }
+
+ // Check if this is a continuation line. If so, read the
+ // entire line and add it to the previous line value.
+ if (buf[0] == ' ')
+ {
+ if (lastName == null)
+ {
+ throw new IOException(
+ "Manifest syntax: Invalid line continuation - "
+ + new String(buf, 0, 0, len));
+ }
+ byte[] lastValue = tmpMap.get(lastName);
+ byte[] tmp = new byte[lastValue.length + len - 1];
+ System.arraycopy(lastValue, 0, tmp, 0, lastValue.length);
+ System.arraycopy(buf, 1, tmp, lastValue.length, len - 1);
+ tmpMap.put(lastName, tmp);
+ }
+ // Otherwise, try to find the attribute name and its value.
+ else
+ {
+ for (int i = 0; i < len; i++)
+ {
+ // If we are at the end, then this must be an error.
+ if (i == (len - 1))
+ {
+ throw new IOException(
+ "Manifest syntax: Invalid attribute name - "
+ + new String(buf, 0, 0, len));
+ }
+ // We found the end of the attribute name
+ else if (buf[i] == ':')
+ {
+ // Make sure the header has a space separator.
+ if (buf[i + 1] != ' ')
+ {
+ throw new IOException(
+ "Manifest syntax: Header space separator missing - "
+ + new String(buf, 0, 0, len));
+ }
+ // Convert attribute name to a string.
+ lastName = new String(buf, 0, 0, i);
+ byte[] tmp = new byte[len - i - 2];
+ System.arraycopy(buf, i + 2, tmp, 0, len - i - 2);
+ byte[] old = tmpMap.put(lastName, tmp);
+ if (old != null)
+ {
+ throw new IllegalArgumentException(
+ "Manifest syntax: Duplicate header - "
+ + new String(buf, 0, 0, len));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ for (Entry<String, byte[]> entry : tmpMap.entrySet())
+ {
+ byte[] value = entry.getValue();
+ mainAttrs.put(
+ entry.getKey(),
+ new String(value, 0, value.length, "UTF8"));
+ }
+
+ }
+ finally
+ {
+ is.close();
+ }
+
+ return mainAttrs;
+ }
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 1410f0f..b806210 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -583,15 +583,15 @@
}
}
- public JarFileX openJAR(File file) throws IOException
+ public ZipFileX openZipFile(File file) throws IOException
{
if (System.getSecurityManager() != null)
{
try
{
Actions actions = (Actions) m_actions.get();
- actions.set(Actions.OPEN_JARX_ACTION, file);
- return (JarFileX) AccessController.doPrivileged(actions, m_acc);
+ actions.set(Actions.OPEN_ZIPFILE_ACTION, file);
+ return (ZipFileX) AccessController.doPrivileged(actions, m_acc);
}
catch (PrivilegedActionException ex)
{
@@ -604,32 +604,7 @@
}
else
{
- return new JarFileX(file);
- }
- }
-
- public JarFileX openJAR(File file, boolean verify) throws IOException
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- Actions actions = (Actions) m_actions.get();
- actions.set(Actions.OPEN_JARX_VERIFY_ACTION, file, (verify ? Boolean.TRUE : Boolean.FALSE));
- return (JarFileX) AccessController.doPrivileged(actions, m_acc);
- }
- catch (PrivilegedActionException ex)
- {
- if (ex.getException() instanceof IOException)
- {
- throw (IOException) ex.getException();
- }
- throw (RuntimeException) ex.getException();
- }
- }
- else
- {
- return new JarFileX(file, verify);
+ return new ZipFileX(file);
}
}
@@ -1097,17 +1072,16 @@
public static final int LIST_DIRECTORY_ACTION = 27;
public static final int MAKE_DIRECTORIES_ACTION = 28;
public static final int MAKE_DIRECTORY_ACTION = 29;
- public static final int OPEN_JARX_ACTION = 30;
- public static final int OPEN_JARX_VERIFY_ACTION = 31;
- public static final int OPEN_URLCONNECTION_ACTION = 32;
- public static final int RENAME_FILE_ACTION = 33;
- public static final int SET_ACCESSIBLE_ACTION = 34;
- public static final int START_ACTIVATOR_ACTION = 35;
- public static final int STOP_ACTIVATOR_ACTION = 36;
- public static final int SWAP_FIELD_ACTION = 37;
- public static final int SYSTEM_EXIT_ACTION = 38;
- public static final int FLUSH_FIELD_ACTION = 39;
- public static final int GET_CLASS_LOADER_ACTION = 40;
+ public static final int OPEN_ZIPFILE_ACTION = 30;
+ public static final int OPEN_URLCONNECTION_ACTION = 31;
+ public static final int RENAME_FILE_ACTION = 32;
+ public static final int SET_ACCESSIBLE_ACTION = 33;
+ public static final int START_ACTIVATOR_ACTION = 34;
+ public static final int STOP_ACTIVATOR_ACTION = 35;
+ public static final int SWAP_FIELD_ACTION = 36;
+ public static final int SYSTEM_EXIT_ACTION = 37;
+ public static final int FLUSH_FIELD_ACTION = 38;
+ public static final int GET_CLASS_LOADER_ACTION = 39;
private int m_action = -1;
private Object m_arg1 = null;
@@ -1256,10 +1230,8 @@
return ((File) arg1).mkdirs() ? Boolean.TRUE : Boolean.FALSE;
case MAKE_DIRECTORY_ACTION:
return ((File) arg1).mkdir() ? Boolean.TRUE : Boolean.FALSE;
- case OPEN_JARX_ACTION:
- return new JarFileX((File) arg1);
- case OPEN_JARX_VERIFY_ACTION:
- return new JarFileX((File) arg1, ((Boolean) arg2).booleanValue());
+ case OPEN_ZIPFILE_ACTION:
+ return new ZipFileX((File) arg1);
case OPEN_URLCONNECTION_ACTION:
return ((URL) arg1).openConnection();
case RENAME_FILE_ACTION:
diff --git a/framework/src/main/java/org/apache/felix/framework/util/JarFileX.java b/framework/src/main/java/org/apache/felix/framework/util/ZipFileX.java
similarity index 64%
rename from framework/src/main/java/org/apache/felix/framework/util/JarFileX.java
rename to framework/src/main/java/org/apache/felix/framework/util/ZipFileX.java
index 5174529..550a680 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/JarFileX.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/ZipFileX.java
@@ -21,41 +21,32 @@
import java.io.File;
import java.io.IOException;
import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
/**
* The purpose of this class is to fix an apparent bug in the JVM in versions
* 1.4.2 and lower where directory entries in ZIP/JAR files are not correctly
* identified.
**/
-public class JarFileX extends JarFile
+public class ZipFileX extends ZipFile
{
- public JarFileX(File file) throws IOException
+ public ZipFileX(File file) throws IOException
{
super(file);
}
- public JarFileX(File file, boolean verify) throws IOException
+
+ public ZipFileX(File file, int mode) throws IOException
{
- super(file, verify);
+ super(file, mode);
}
- public JarFileX(File file, boolean verify, int mode) throws IOException
- {
- super(file, verify, mode);
- }
-
- public JarFileX(String name) throws IOException
+ public ZipFileX(String name) throws IOException
{
super(name);
}
- public JarFileX(String name, boolean verify) throws IOException
- {
- super(name, verify);
- }
-
public ZipEntry getEntry(String name)
{
ZipEntry entry = super.getEntry(name);
@@ -69,18 +60,4 @@
}
return entry;
}
-
- public JarEntry getJarEntry(String name)
- {
- JarEntry entry = super.getJarEntry(name);
- if ((entry != null) && (entry.getSize() == 0) && !entry.isDirectory())
- {
- JarEntry dirEntry = super.getJarEntry(name + '/');
- if (dirEntry != null)
- {
- entry = dirEntry;
- }
- }
- return entry;
- }
}
\ No newline at end of file