Refactored the IContent abstraction from the module loader layer to provide
more generic access to the content of the module, which then makes it possible
for IContentLoader implementations to interpret the content as they see fit;
previously, the content loader impl's bundle class path interpretation was
not self-contained and had to be handled externally. The main reason this
was necessary was to make it possible for the content loader to calculate
the class path of a bundle itself and to make it possible that the bundle's
class path could span multiple module's, which will be necessary for fragments
(FELIX-29). This patch refactors the bundle cache to implement the new
IContent mechanisms.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@632420 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index c7fdce9..fc8e96d 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -607,7 +607,11 @@
};
}
- public byte[] getEntry(String name)
+ public boolean hasEntry(String name) {
+ return false;
+ }
+
+ public byte[] getEntryAsBytes(String name)
{
return null;
}
@@ -617,7 +621,13 @@
return null;
}
- public boolean hasEntry(String name) {
- return false;
+ public IContent getEntryAsContent(String name)
+ {
+ return null;
}
-}
+
+ public String getEntryAsNativeLibrary(String name)
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index c528733..2176fc6 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -3432,7 +3432,7 @@
(isExtensionBundle) ? null : mp.getCapabilities(),
mp.getRequirements(),
mp.getDynamicRequirements(),
- mp.getLibraries(m_cache.getArchive(targetId).getRevision(revision)));
+ mp.getLibraries());
// Create the module using the module definition.
IModule module = m_factory.createModule(
@@ -3440,9 +3440,7 @@
// Create the content loader from the module archive.
IContentLoader contentLoader = new ContentLoaderImpl(
- m_logger,
- m_cache.getArchive(targetId).getRevision(revision).getContent(),
- m_cache.getArchive(targetId).getRevision(revision).getContentPath());
+ m_logger, m_cache.getArchive(targetId).getRevision(revision).getContent());
// Set the content loader's search policy.
contentLoader.setSearchPolicy(
new R4SearchPolicy(m_policyCore, module));
@@ -3455,6 +3453,26 @@
// Set the module's content loader to the created content loader.
m_factory.setContentLoader(module, contentLoader);
+ // Verify that all native libraries exist in advance; this will
+ // throw an exception if the native library does not exist.
+ // TODO: CACHE - It would be nice if this check could be done
+ // some place else in the module, perhaps.
+ R4Library[] libs = md.getLibraries();
+ for (int i = 0; (libs != null) && (i < libs.length); i++)
+ {
+ String entryName = libs[i].getEntryName();
+ if (entryName != null)
+ {
+ if (contentLoader.getContent().getEntryAsNativeLibrary(entryName) == null)
+ {
+ // The content loader was opened when trying to find the libraries,
+ // so make sure to close it since it will be deleted.
+ contentLoader.close();
+ throw new BundleException("Native library does not exist: " + entryName);
+ }
+ }
+ }
+
// Done, so return the module.
return module;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
index 70911e7..51d327f 100644
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
@@ -61,16 +61,6 @@
return null;
}
- public IContent[] getContentPath() throws Exception
- {
- return null;
- }
-
- public String findLibrary(String libName) throws Exception
- {
- return null;
- }
-
public void dispose() throws Exception
{
}
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
index 3089788..59bc7ce 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleRevision.java
@@ -72,11 +72,10 @@
m_location = location;
}
-
/**
* <p>
* Returns the logger for this revision.
- * <p>
+ * </p>
* @return the logger instance for this revision.
**/
public Logger getLogger()
@@ -116,39 +115,10 @@
**/
public abstract Map getManifestHeader() throws Exception;
- /**
- * <p>
- * Returns a content object that is associated with the revision.
- * </p>
- * @return a content object that is associated with the revision.
- * @throws java.lang.Exception if any error occurs.
- **/
public abstract IContent getContent() throws Exception;
/**
* <p>
- * Returns an array of content objects that are associated with the
- * specified revision's bundle class path.
- * </p>
- * @return an array of content objects for the revision's bundle class path.
- * @throws java.lang.Exception if any error occurs.
- **/
- public abstract IContent[] getContentPath() throws Exception;
-
- /**
- * <p>
- * Returns the absolute file path for the specified native library of the
- * revision.
- * </p>
- * @param libName the name of the library.
- * @return a <tt>String</tt> that contains the absolute path name to
- * the requested native library of the revision.
- * @throws java.lang.Exception if any error occurs.
- **/
- public abstract String findLibrary(String libName) throws Exception;
-
- /**
- * <p>
* This method is called when the revision is no longer needed. The directory
* associated with the revision will automatically be removed for each
* revision, so this method only needs to be concerned with other issues,
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
similarity index 79%
rename from framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java
rename to framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
index 1aa0674..c4d2301 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/ContentDirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/ContentDirectoryContent.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.moduleloader;
+package org.apache.felix.framework.cache;
+import org.apache.felix.moduleloader.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
@@ -37,14 +38,6 @@
? path + "/" : path;
}
- protected void finalize()
- {
- if (m_content != null)
- {
- m_content.close();
- }
- }
-
public void open()
{
m_content.open();
@@ -53,18 +46,10 @@
public synchronized void close()
{
- try
- {
- if (m_content != null)
- {
- m_content.close();
- }
- }
- catch (Exception ex)
- {
- System.err.println("JarContent: " + ex);
- }
-
+ // We do not actually close the associated content
+ // from which we are filtering our directory because
+ // we assume that this will be close manually by
+ // the owner of that content.
m_content = null;
m_opened = false;
}
@@ -84,7 +69,17 @@
return m_content.hasEntry(m_rootPath + name);
}
- public synchronized byte[] getEntry(String name) throws IllegalStateException
+ public synchronized Enumeration getEntries()
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("ContentDirectoryContent is not open");
+ }
+
+ return new EntriesEnumeration(m_content.getEntries(), m_rootPath);
+ }
+
+ public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
{
if (!m_opened)
{
@@ -96,7 +91,7 @@
name = name.substring(1);
}
- return m_content.getEntry(m_rootPath + name);
+ return m_content.getEntryAsBytes(m_rootPath + name);
}
public synchronized InputStream getEntryAsStream(String name)
@@ -115,14 +110,34 @@
return m_content.getEntryAsStream(m_rootPath + name);
}
- public synchronized Enumeration getEntries()
+ public IContent getEntryAsContent(String name)
{
if (!m_opened)
{
throw new IllegalStateException("ContentDirectoryContent is not open");
}
- return new EntriesEnumeration(m_content.getEntries(), m_rootPath);
+ if ((name.length() > 0) && (name.charAt(0) == '/'))
+ {
+ name = name.substring(1);
+ }
+
+ return m_content.getEntryAsContent(m_rootPath + name);
+ }
+
+ public String getEntryAsNativeLibrary(String name)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("ContentDirectoryContent is not open");
+ }
+
+ if ((name.length() > 0) && (name.charAt(0) == '/'))
+ {
+ name = name.substring(1);
+ }
+
+ return m_content.getEntryAsNativeLibrary(m_rootPath + name);
}
public String toString()
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
similarity index 66%
rename from framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java
rename to framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
index fb8365f..feed684 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/DirectoryContent.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -16,27 +16,33 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.moduleloader;
+package org.apache.felix.framework.cache;
+import org.apache.felix.moduleloader.*;
import java.io.*;
import java.util.*;
+import org.apache.felix.framework.Logger;
public class DirectoryContent implements IContent
{
private static final int BUFSIZE = 4096;
+ private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+ private static final transient String LIBRARY_DIRECTORY = "lib";
- private File m_dir = null;
+ private Logger m_logger;
+ private Object m_revisionLock;
+ private File m_rootDir;
+ private File m_dir;
private boolean m_opened = false;
- public DirectoryContent(File dir)
+ public DirectoryContent(Logger logger, Object revisionLock, File rootDir, File dir)
{
+ m_logger = logger;
+ m_revisionLock = revisionLock;
+ m_rootDir = rootDir;
m_dir = dir;
}
- protected void finalize()
- {
- }
-
public void open()
{
m_opened = true;
@@ -62,7 +68,21 @@
return new File(m_dir, name).exists();
}
- public synchronized byte[] getEntry(String name) throws IllegalStateException
+ public synchronized Enumeration getEntries()
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Wrap entries enumeration to filter non-matching entries.
+ Enumeration e = new EntriesEnumeration(m_dir);
+
+ // Spec says to return null if there are no entries.
+ return (e.hasMoreElements()) ? e : null;
+ }
+
+ public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
{
if (!m_opened)
{
@@ -130,18 +150,64 @@
return new FileInputStream(new File(m_dir, name));
}
- public synchronized Enumeration getEntries()
+ public synchronized IContent getEntryAsContent(String entryName)
{
if (!m_opened)
{
- throw new IllegalStateException("JarContent is not open");
+ throw new IllegalStateException("DirectoryContent is not open");
}
- // Wrap entries enumeration to filter non-matching entries.
- Enumeration e = new EntriesEnumeration(m_dir);
+ // Remove any leading slash, since all bundle class path
+ // entries are relative to the root of the bundle.
+ entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
- // Spec says to return null if there are no entries.
- return (e.hasMoreElements()) ? e : null;
+ // Any embedded JAR files will be extracted to the embedded directory.
+ File embedDir = new File(m_rootDir, m_dir.getName() + EMBEDDED_DIRECTORY);
+
+ // 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.
+ File file = new File(m_dir, entryName);
+ if (BundleCache.getSecureAction().isFileDirectory(file))
+ {
+ return new DirectoryContent(m_logger, m_revisionLock, m_rootDir, file);
+ }
+ else if (BundleCache.getSecureAction().fileExists(file)
+ && entryName.endsWith(".jar"))
+ {
+ File extractedDir = new File(embedDir,
+ (entryName.lastIndexOf('/') >= 0)
+ ? entryName.substring(0, entryName.lastIndexOf('/'))
+ : entryName);
+ synchronized (m_revisionLock)
+ {
+ if (!BundleCache.getSecureAction().fileExists(extractedDir))
+ {
+ if (!BundleCache.getSecureAction().mkdirs(extractedDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to extract embedded directory.");
+ }
+ }
+ }
+ System.out.println("+++ EXTRACTED JAR DIR " + extractedDir);
+ return new JarContent(m_logger, m_revisionLock, extractedDir, file);
+ }
+
+ // The entry could not be found, so return null.
+ return null;
+ }
+
+// TODO: This will need to consider security.
+ public synchronized String getEntryAsNativeLibrary(String name)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("DirectoryContent is not open");
+ }
+
+ return BundleCache.getSecureAction().getAbsolutePath(new File(m_rootDir, name));
}
public String toString()
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
index b235a2e..94ee30d 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/DirectoryRevision.java
@@ -30,9 +30,9 @@
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
-import org.apache.felix.moduleloader.DirectoryContent;
+import org.apache.felix.framework.cache.DirectoryContent;
import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.JarContent;
+import org.apache.felix.framework.cache.JarContent;
/**
* <p>
@@ -43,8 +43,6 @@
**/
class DirectoryRevision extends BundleRevision
{
- private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
-
private File m_refDir = null;
private Map m_header = null;
@@ -109,90 +107,9 @@
}
}
- public IContent getContent() throws Exception
+ public synchronized IContent getContent() throws Exception
{
- return new DirectoryContent(m_refDir);
- }
-
- public synchronized IContent[] getContentPath() throws Exception
- {
- // Creating the content path entails examining the bundle's
- // class path to determine whether the bundle JAR file itself
- // is on the bundle's class path and then creating content
- // objects for everything on the class path.
-
- // Get the bundle's manifest header.
- Map map = getManifestHeader();
-
- // Find class path meta-data.
- String classPath = (map == null)
- ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-
- // Parse the class path into strings.
- String[] classPathStrings = ManifestParser.parseDelimitedString(
- classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-
- if (classPathStrings == null)
- {
- classPathStrings = new String[0];
- }
-
- // Create the bundles class path.
- IContent self = new DirectoryContent(m_refDir);
- List contentList = new ArrayList();
- for (int i = 0; i < classPathStrings.length; i++)
- {
- // Remove any leading slash, since all bundle class path
- // entries are relative to the root of the bundle.
- classPathStrings[i] = (classPathStrings[i].startsWith("/"))
- ? classPathStrings[i].substring(1)
- : classPathStrings[i];
-
- // Check for the bundle itself on the class path.
- if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
- {
- contentList.add(self);
- }
- else
- {
- // Determine if the class path entry is a file or directory.
- File file = new File(m_refDir, classPathStrings[i]);
- if (BundleCache.getSecureAction().isFileDirectory(file))
- {
- contentList.add(new DirectoryContent(file));
- }
- else
- {
- // Ignore any entries that do not exist per the spec.
- if (BundleCache.getSecureAction().fileExists(file))
- {
- contentList.add(new JarContent(file));
- }
- }
- }
- }
-
- // If there is nothing on the class path, then include
- // "." by default, as per the spec.
- if (contentList.size() == 0)
- {
- contentList.add(self);
- }
-
- return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
- }
-
-// TODO: This will need to consider security.
- public String findLibrary(String libName) throws Exception
- {
- String result = BundleCache.getSecureAction().getAbsolutePath(new File(m_refDir, libName));
-
- if (result == null)
- {
- throw new IOException("No such file: " + libName);
- }
-
- return result;
+ return new DirectoryContent(getLogger(), this, getRevisionRootDir(), m_refDir);
}
public void dispose() throws Exception
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
new file mode 100644
index 0000000..ba317a8
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
@@ -0,0 +1,596 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.cache;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.JarFileX;
+import org.apache.felix.moduleloader.IContent;
+
+public class JarContent implements IContent
+{
+ private static final int BUFSIZE = 4096;
+ private static final transient String LEGACY_EMBEDDED_DIRECTORY = "embedded";
+ private static final transient String EMBEDDED_DIRECTORY = "-embedded";
+ private static final transient String LIBRARY_DIRECTORY = "lib";
+
+ private final Logger m_logger;
+ private final Object m_revisionLock;
+ private final File m_rootDir;
+ private final File m_file;
+ private JarFileX m_jarFile = null;
+ // TODO: CACHE - It would be nice to eventually remove this legacy flag.
+ private final boolean m_legacy;
+ private boolean m_opened = false;
+
+ public JarContent(Logger logger, Object revisionLock, File rootDir, File file)
+ {
+ m_logger = logger;
+ m_revisionLock = revisionLock;
+ m_rootDir = rootDir;
+ m_file = file;
+ m_legacy = false;
+ }
+
+ // This is only used by JarRevision.
+ public JarContent(Logger logger, Object revisionLock, File rootDir, File file, boolean legacy)
+ {
+ m_logger = logger;
+ m_revisionLock = revisionLock;
+ m_rootDir = rootDir;
+ m_file = file;
+ m_legacy = legacy;
+ }
+
+ protected void finalize()
+ {
+ if (m_jarFile != null)
+ {
+ try
+ {
+ m_jarFile.close();
+ }
+ catch (IOException ex)
+ {
+ // Not much we can do, so ignore it.
+ }
+ }
+ }
+
+ public synchronized void open()
+ {
+ m_opened = true;
+ }
+
+ public synchronized void close()
+ {
+ try
+ {
+ if (m_jarFile != null)
+ {
+ m_jarFile.close();
+ }
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ }
+
+ m_jarFile = null;
+ m_opened = false;
+ }
+
+ public synchronized boolean hasEntry(String name) throws IllegalStateException
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return false;
+ }
+ }
+
+ try
+ {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ return ze != null;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ finally
+ {
+ }
+ }
+
+ public synchronized Enumeration getEntries()
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return null;
+ }
+ }
+
+ // Wrap entries enumeration to filter non-matching entries.
+ Enumeration e = new EntriesEnumeration(m_jarFile.entries());
+
+ // Spec says to return null if there are no entries.
+ return (e.hasMoreElements()) ? e : null;
+ }
+
+ public synchronized byte[] getEntryAsBytes(String name) throws IllegalStateException
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return null;
+ }
+ }
+
+ // Get the embedded resource.
+ InputStream is = null;
+ ByteArrayOutputStream baos = null;
+
+ try
+ {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ if (ze == null)
+ {
+ return null;
+ }
+ is = m_jarFile.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();
+
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to read bytes.", ex);
+ return null;
+ }
+ finally
+ {
+ try
+ {
+ if (baos != null) baos.close();
+ }
+ catch (Exception ex)
+ {
+ }
+ try
+ {
+ if (is != null) is.close();
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+
+ public synchronized InputStream getEntryAsStream(String name)
+ throws IllegalStateException, IOException
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return null;
+ }
+ }
+
+ // Get the embedded resource.
+ InputStream is = null;
+
+ try
+ {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ if (ze == null)
+ {
+ return null;
+ }
+ is = m_jarFile.getInputStream(ze);
+ if (is == null)
+ {
+ return null;
+ }
+ }
+ catch (Exception ex)
+ {
+ return null;
+ }
+
+ return is;
+ }
+
+ public synchronized IContent getEntryAsContent(String entryName)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return null;
+ }
+
+ }
+
+ // Remove any leading slash.
+ entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
+
+ // Any embedded JAR files will be extracted to the embedded directory.
+ // Since embedded JAR file names may clash when extracting from multiple
+ // embedded JAR files, the embedded directory is per embedded JAR file.
+ // For backwards compatibility purposes, don't use the file cache name
+ // for the root bundle JAR file.
+ File embedDir;
+ if (m_legacy)
+ {
+ embedDir = new File(m_rootDir, LEGACY_EMBEDDED_DIRECTORY);
+ }
+ else
+ {
+ embedDir = new File(m_rootDir, m_file.getName() + EMBEDDED_DIRECTORY);
+ }
+
+ // Find the entry in the JAR file and create the
+ // appropriate content type for it.
+
+ // 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);
+ if ((ze != null) && ze.isDirectory())
+ {
+ File extractedDir = new File(embedDir, entryName);
+
+ // Extracting an embedded directory file impacts all other existing
+ // contents for this revision, so we have to grab the revision
+ // lock first before trying to create a directory for an embedded
+ // directory to avoid a race condition.
+ synchronized (m_revisionLock)
+ {
+ if (!BundleCache.getSecureAction().fileExists(extractedDir))
+ {
+ if (!BundleCache.getSecureAction().mkdirs(extractedDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to extract embedded directory.");
+ }
+ }
+ }
+ return new ContentDirectoryContent(this, entryName);
+ }
+ else if ((ze != null) && ze.getName().endsWith(".jar"))
+ {
+ File extractedJar = new File(embedDir, entryName);
+
+ // Extracting the embedded JAR file impacts all other existing
+ // contents for this revision, so we have to grab the revision
+ // lock first before trying to extract the embedded JAR file
+ // to avoid a race condition.
+ synchronized (m_revisionLock)
+ {
+ if (!BundleCache.getSecureAction().fileExists(extractedJar))
+ {
+ try
+ {
+ extractEmbeddedJar(entryName);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to extract embedded JAR file.", ex);
+ }
+ }
+ }
+ return new JarContent(
+ m_logger, m_revisionLock, extractedJar.getParentFile(), extractedJar);
+ }
+
+ // The entry could not be found, so return null.
+ return null;
+ }
+
+// TODO: This will need to consider security.
+ public synchronized String getEntryAsNativeLibrary(String name)
+ {
+ if (!m_opened)
+ {
+ throw new IllegalStateException("JarContent is not open");
+ }
+
+ // Open JAR file if not already opened.
+ if (m_jarFile == null)
+ {
+ try
+ {
+ openJarFile();
+ }
+ catch (IOException ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to open JAR file.", ex);
+ return null;
+ }
+ }
+
+ // Get bundle lib directory.
+ File libDir = new File(m_rootDir, LIBRARY_DIRECTORY);
+ // Get lib file.
+ File libFile = new File(libDir, File.separatorChar + name);
+ // Make sure that the library's parent directory exists;
+ // it may be in a sub-directory.
+ libDir = libFile.getParentFile();
+ if (!BundleCache.getSecureAction().fileExists(libDir))
+ {
+ if (!BundleCache.getSecureAction().mkdirs(libDir))
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Unable to create library directory.");
+ return null;
+ }
+ }
+ // Extract the library from the JAR file if it does not
+ // already exist.
+ if (!BundleCache.getSecureAction().fileExists(libFile))
+ {
+ InputStream is = null;
+
+ try
+ {
+ ZipEntry ze = m_jarFile.getEntry(name);
+ if (ze == null)
+ {
+ return null;
+ }
+ is = new BufferedInputStream(
+ m_jarFile.getInputStream(ze), BundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + name);
+ }
+
+ // Create the file.
+ BundleCache.copyStreamToFile(is, libFile);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "JarContent: Extracting native library.", ex);
+ }
+ finally
+ {
+ try
+ {
+ if (is != null) is.close();
+ }
+ catch (IOException ex)
+ {
+ // Not much we can do.
+ }
+ }
+ }
+
+ return BundleCache.getSecureAction().getAbsolutePath(libFile);
+ }
+
+ public String toString()
+ {
+ return "JAR " + m_file.getPath();
+ }
+
+ public File getFile()
+ {
+ return m_file;
+ }
+
+ private void openJarFile() throws IOException
+ {
+ if (m_jarFile == null)
+ {
+ m_jarFile = BundleCache.getSecureAction().openJAR(m_file);
+ }
+ }
+
+ /**
+ * 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)
+ throws Exception
+ {
+ // Remove leading slash if present.
+ jarPath = (jarPath.length() > 0) && (jarPath.charAt(0) == '/')
+ ? jarPath.substring(1) : jarPath;
+
+ // Any embedded JAR files will be extracted to the embedded directory.
+ // Since embedded JAR file names may clash when extracting from multiple
+ // embedded JAR files, the embedded directory is per embedded JAR file.
+ // For backwards compatibility purposes, don't use the file cache name
+ // for the root bundle JAR file.
+ File embedDir;
+ if (m_legacy)
+ {
+ embedDir = new File(m_rootDir, LEGACY_EMBEDDED_DIRECTORY);
+ }
+ else
+ {
+ embedDir = new File(m_rootDir, m_file.getName() + EMBEDDED_DIRECTORY);
+ }
+ File jarFile = new File(embedDir, jarPath);
+
+ if (!BundleCache.getSecureAction().fileExists(jarFile))
+ {
+ InputStream is = null;
+ try
+ {
+ // Make sure class path entry is a JAR file.
+ ZipEntry ze = m_jarFile.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())
+ {
+ // 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_jarFile.getInputStream(ze), BundleCache.BUFSIZE);
+ if (is == null)
+ {
+ throw new IOException("No input stream: " + jarPath);
+ }
+ // Copy the file.
+ BundleCache.copyStreamToFile(is, jarFile);
+ }
+ }
+ finally
+ {
+ if (is != null) is.close();
+ }
+ }
+ }
+
+ private static class EntriesEnumeration implements Enumeration
+ {
+ private Enumeration m_enumeration = null;
+
+ public EntriesEnumeration(Enumeration enumeration)
+ {
+ m_enumeration = enumeration;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return m_enumeration.hasMoreElements();
+ }
+
+ public Object nextElement()
+ {
+ return ((ZipEntry) m_enumeration.nextElement()).getName();
+ }
+ }
+}
\ No newline at end of file
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 76b8763..84c3ebb 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,28 +18,19 @@
*/
package org.apache.felix.framework.cache;
-import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
-import java.security.PrivilegedActionException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.ManifestParser;
-import org.apache.felix.moduleloader.ContentDirectoryContent;
import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.JarContent;
/**
* <p>
@@ -55,8 +46,6 @@
class JarRevision extends BundleRevision
{
private static final transient String BUNDLE_JAR_FILE = "bundle.jar";
- private static final transient String EMBEDDED_DIRECTORY = "embedded";
- private static final transient String LIBRARY_DIRECTORY = "lib";
private File m_bundleFile = null;
private Map m_header = null;
@@ -122,142 +111,9 @@
}
}
- public IContent getContent() throws Exception
+ public synchronized IContent getContent() throws Exception
{
- return new JarContent(m_bundleFile);
- }
-
- public synchronized IContent[] getContentPath() throws Exception
- {
- // Creating the content path entails examining the bundle's
- // class path to determine whether the bundle JAR file itself
- // is on the bundle's class path and then creating content
- // objects for everything on the class path.
-
- File embedDir = new File(getRevisionRootDir(), EMBEDDED_DIRECTORY);
-
- // Get the bundle's manifest header.
- Map map = getManifestHeader();
-
- // Find class path meta-data.
- String classPath = (map == null)
- ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-
- // Parse the class path into strings.
- String[] classPathStrings = ManifestParser.parseDelimitedString(
- classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-
- if (classPathStrings == null)
- {
- classPathStrings = new String[0];
- }
-
- // Create the bundles class path.
- JarFile bundleJar = null;
- try
- {
- bundleJar = BundleCache.getSecureAction().openJAR(m_bundleFile);
- IContent self = new JarContent(m_bundleFile);
- List contentList = new ArrayList();
- for (int i = 0; i < classPathStrings.length; i++)
- {
- // Remove any leading slash, since all bundle class path
- // entries are relative to the root of the bundle.
- classPathStrings[i] = (classPathStrings[i].startsWith("/"))
- ? classPathStrings[i].substring(1)
- : classPathStrings[i];
-
- // Check for the bundle itself on the class path.
- if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
- {
- contentList.add(self);
- }
- else
- {
- // Determine if the class path entry is a file or directory
- // in the bundle JAR file.
- ZipEntry entry = bundleJar.getEntry(classPathStrings[i]);
- if ((entry != null) && entry.isDirectory())
- {
- contentList.add(new ContentDirectoryContent(self, classPathStrings[i]));
- }
- else
- {
- // Ignore any entries that do not exist per the spec.
- File extractedJar = new File(embedDir, classPathStrings[i]);
- if (BundleCache.getSecureAction().fileExists(extractedJar))
- {
- contentList.add(new JarContent(extractedJar));
- }
- }
- }
- }
-
- // If there is nothing on the class path, then include
- // "." by default, as per the spec.
- if (contentList.size() == 0)
- {
- contentList.add(self);
- }
-
- return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
- }
- finally
- {
- if (bundleJar != null) bundleJar.close();
- }
- }
-
-// TODO: This will need to consider security.
- public synchronized String findLibrary(String libName) throws Exception
- {
- // Get bundle lib directory.
- File libDir = new File(getRevisionRootDir(), LIBRARY_DIRECTORY);
- // Get lib file.
- File libFile = new File(libDir, File.separatorChar + libName);
- // Make sure that the library's parent directory exists;
- // it may be in a sub-directory.
- libDir = libFile.getParentFile();
- if (!BundleCache.getSecureAction().fileExists(libDir))
- {
- if (!BundleCache.getSecureAction().mkdirs(libDir))
- {
- throw new IOException("Unable to create library directory.");
- }
- }
- // Extract the library from the JAR file if it does not
- // already exist.
- if (!BundleCache.getSecureAction().fileExists(libFile))
- {
- JarFile bundleJar = null;
- InputStream is = null;
-
- try
- {
- bundleJar = BundleCache.getSecureAction().openJAR(m_bundleFile);
- ZipEntry ze = bundleJar.getEntry(libName);
- if (ze == null)
- {
- throw new IOException("No JAR entry: " + libName);
- }
- is = new BufferedInputStream(
- bundleJar.getInputStream(ze), BundleCache.BUFSIZE);
- if (is == null)
- {
- throw new IOException("No input stream: " + libName);
- }
-
- // Create the file.
- BundleCache.copyStreamToFile(is, libFile);
- }
- finally
- {
- if (bundleJar != null) bundleJar.close();
- if (is != null) is.close();
- }
- }
-
- return BundleCache.getSecureAction().getAbsolutePath(libFile);
+ return new JarContent(getLogger(), this, getRevisionRootDir(), m_bundleFile, true);
}
public void dispose() throws Exception
@@ -320,147 +176,10 @@
// Save the bundle jar file.
BundleCache.copyStreamToFile(is, m_bundleFile);
}
-
- preprocessBundleJar();
}
finally
{
if (is != null) is.close();
}
}
-
- /**
- * This method pre-processes a bundle JAR file making it ready
- * for use. This entails extracting all embedded JAR files and
- * all native libraries.
- * @throws java.lang.Exception if any error occurs while processing JAR file.
- **/
- private void preprocessBundleJar() throws Exception
- {
- //
- // Create special directories so that we can avoid checking
- // for their existence all the time.
- //
-
- File embedDir = new File(getRevisionRootDir(), EMBEDDED_DIRECTORY);
- if (!BundleCache.getSecureAction().fileExists(embedDir))
- {
- if (!BundleCache.getSecureAction().mkdir(embedDir))
- {
- throw new IOException("Could not create embedded JAR directory.");
- }
- }
-
- File libDir = new File(getRevisionRootDir(), LIBRARY_DIRECTORY);
- if (!BundleCache.getSecureAction().fileExists(libDir))
- {
- if (!BundleCache.getSecureAction().mkdir(libDir))
- {
- throw new IOException("Unable to create native library directory.");
- }
- }
-
- //
- // This block extracts all embedded JAR files.
- //
-
- try
- {
- // Get the bundle's manifest header.
- Map map = getManifestHeader();
-
- // Find class path meta-data.
- String classPath = (map == null)
- ? null : (String) map.get(FelixConstants.BUNDLE_CLASSPATH);
-
- // Parse the class path into strings.
- String[] classPathStrings = ManifestParser.parseDelimitedString(
- classPath, FelixConstants.CLASS_PATH_SEPARATOR);
-
- if (classPathStrings == null)
- {
- classPathStrings = new String[0];
- }
-
- for (int i = 0; i < classPathStrings.length; i++)
- {
- if (!classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
- {
- extractEmbeddedJar(classPathStrings[i]);
- }
- }
-
- }
- catch (PrivilegedActionException ex)
- {
- throw ex.getException();
- }
- }
-
- /**
- * 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)
- throws Exception
- {
- // Remove leading slash if present.
- jarPath = (jarPath.length() > 0) && (jarPath.charAt(0) == '/')
- ? jarPath.substring(1) : jarPath;
-
- // If JAR is already extracted, then don't re-extract it...
- File jarFile = new File(
- getRevisionRootDir(), EMBEDDED_DIRECTORY + File.separatorChar + jarPath);
-
- if (!BundleCache.getSecureAction().fileExists(jarFile))
- {
- JarFile bundleJar = null;
- InputStream is = null;
- try
- {
- // Make sure class path entry is a JAR file.
- bundleJar = BundleCache.getSecureAction().openJAR(m_bundleFile);
- ZipEntry ze = bundleJar.getEntry(jarPath);
- if (ze == null)
- {
-// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
-// need to create an "Eventer" class like "Logger" perhaps.
- getLogger().log(Logger.LOG_INFO, "Class path entry not found: " + jarPath);
- 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))
- {
- if (!BundleCache.getSecureAction().mkdirs(jarDir))
- {
- throw new IOException("Unable to create embedded JAR directory.");
- }
- }
-
- // Extract embedded JAR into its directory.
- is = new BufferedInputStream(bundleJar.getInputStream(ze), BundleCache.BUFSIZE);
- if (is == null)
- {
- throw new IOException("No input stream: " + jarPath);
- }
- // Copy the file.
- BundleCache.copyStreamToFile(is, jarFile);
- }
- }
- finally
- {
- if (bundleJar != null) bundleJar.close();
- if (is != null) is.close();
- }
- }
- }
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentClassLoader.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentClassLoader.java
index bd37810..286834a 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentClassLoader.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentClassLoader.java
@@ -30,7 +30,7 @@
import org.apache.felix.framework.util.Util;
import org.apache.felix.moduleloader.IContent;
import org.apache.felix.moduleloader.IContentLoader;
-import org.apache.felix.moduleloader.JarContent;
+import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.moduleloader.ResourceNotFoundException;
public class ContentClassLoader extends SecureClassLoader
@@ -145,7 +145,7 @@
(bytes == null) &&
(i < m_contentLoader.getClassPath().length); i++)
{
- bytes = m_contentLoader.getClassPath()[i].getEntry(actual);
+ bytes = m_contentLoader.getClassPath()[i].getEntryAsBytes(actual);
content = m_contentLoader.getClassPath()[i];
}
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
index 4d12c90..e6c92ff 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ContentLoaderImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -22,11 +22,18 @@
import java.io.InputStream;
import java.net.URL;
import java.security.ProtectionDomain;
+import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
import java.util.Vector;
+import java.util.jar.Manifest;
import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.SecureAction;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.apache.felix.moduleloader.*;
public class ContentLoaderImpl implements IContentLoader
@@ -40,12 +47,10 @@
private ProtectionDomain m_protectionDomain = null;
private static SecureAction m_secureAction = new SecureAction();
- public ContentLoaderImpl(Logger logger, IContent content,
- IContent[] contentPath)
+ public ContentLoaderImpl(Logger logger, IContent content)
{
m_logger = logger;
m_content = content;
- m_contentPath = contentPath;
}
public Logger getLogger()
@@ -56,6 +61,15 @@
public void open()
{
m_content.open();
+ try
+ {
+ initializeContentPath();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Unable to initialize content path.", ex);
+ }
+
for (int i = 0; (m_contentPath != null) && (i < m_contentPath.length); i++)
{
m_contentPath[i].open();
@@ -100,10 +114,10 @@
{
return m_urlPolicy;
}
-
+
public synchronized void setSecurityContext(Object securityContext)
{
- m_protectionDomain = (ProtectionDomain) securityContext;
+ m_protectionDomain = (ProtectionDomain) securityContext;
}
public synchronized Object getSecurityContext()
@@ -113,7 +127,7 @@
public Class getClass(String name)
{
- synchronized (this)
+ synchronized (this)
{
if (m_classLoader == null)
{
@@ -267,4 +281,82 @@
{
return m_searchPolicy.toString();
}
+
+ private void initializeContentPath() throws Exception
+ {
+ // Creating the content path entails examining the bundle's
+ // class path to determine whether the bundle JAR file itself
+ // is on the bundle's class path and then creating content
+ // objects for everything on the class path.
+
+ // Get the bundle's manifest header.
+ InputStream is = null;
+ Map headers = null;
+ try
+ {
+ is = m_content.getEntryAsStream("META-INF/MANIFEST.MF");
+ headers = new StringMap(new Manifest(is).getMainAttributes(), false);
+ }
+ finally
+ {
+ if (is != null) is.close();
+ }
+
+ // Find class path meta-data.
+ String classPath = (headers == null)
+ ? null : (String) headers.get(FelixConstants.BUNDLE_CLASSPATH);
+
+ // Parse the class path into strings.
+ String[] classPathStrings = ManifestParser.parseDelimitedString(
+ classPath, FelixConstants.CLASS_PATH_SEPARATOR);
+
+ if (classPathStrings == null)
+ {
+ classPathStrings = new String[0];
+ }
+
+ // Create the bundles class path.
+ List contentList = new ArrayList();
+ for (int i = 0; i < classPathStrings.length; i++)
+ {
+ // Remove any leading slash, since all bundle class path
+ // entries are relative to the root of the bundle.
+ classPathStrings[i] = (classPathStrings[i].startsWith("/"))
+ ? classPathStrings[i].substring(1)
+ : classPathStrings[i];
+
+ // Check for the bundle itself on the class path.
+ if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ {
+ contentList.add(m_content);
+ }
+ else
+ {
+ // Determine if the class path entry is a file or directory
+ // in the bundle JAR file.
+ IContent content = m_content.getEntryAsContent(classPathStrings[i]);
+ if (content != null)
+ {
+ contentList.add(content);
+ }
+ else
+ {
+// TODO: FRAMEWORK - Per the spec, this should fire a FrameworkEvent.INFO event;
+// need to create an "Eventer" class like "Logger" perhaps.
+ m_logger.log(Logger.LOG_INFO,
+ "Class path entry not found: "
+ + classPathStrings[i]);
+ }
+ }
+ }
+
+ // If there is nothing on the class path, then include
+ // "." by default, as per the spec.
+ if (contentList.size() == 0)
+ {
+ contentList.add(m_content);
+ }
+
+ m_contentPath = (IContent[]) contentList.toArray(new IContent[contentList.size()]);
+ }
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
index e521e89..f727f6d 100755
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
+++ b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
@@ -833,10 +833,10 @@
R4Library[] libs = module.getDefinition().getLibraries();
for (int i = 0; (libs != null) && (i < libs.length); i++)
{
- String lib = libs[i].getPath(name);
- if (lib != null)
+ if (libs[i].match(name))
{
- return lib;
+ return module.getContentLoader().getContent()
+ .getEntryAsNativeLibrary(libs[i].getEntryName());
}
}
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/JarFileX.java b/framework/src/main/java/org/apache/felix/framework/util/JarFileX.java
similarity index 98%
rename from framework/src/main/java/org/apache/felix/moduleloader/JarFileX.java
rename to framework/src/main/java/org/apache/felix/framework/util/JarFileX.java
index b87ba82..a9b5e3f 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/JarFileX.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/JarFileX.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.moduleloader;
+package org.apache.felix.framework.util;
import java.io.File;
import java.io.IOException;
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 34db5fc..0134238 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
@@ -26,7 +26,7 @@
import org.apache.felix.framework.searchpolicy.ContentClassLoader;
import org.apache.felix.framework.searchpolicy.ContentLoaderImpl;
-import org.apache.felix.moduleloader.JarFileX;
+import org.apache.felix.framework.util.JarFileX;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index e942a79..bc39416 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -220,7 +220,7 @@
// Check to see if there was an optional native library clause, which is
// represented by a null library header; if so, record it and remove it.
if ((m_libraryHeaders.length > 0) &&
- (m_libraryHeaders[m_libraryHeaders.length - 1].getLibraryFiles() == null))
+ (m_libraryHeaders[m_libraryHeaders.length - 1].getLibraryEntries() == null))
{
m_libraryHeadersOptional = true;
R4LibraryClause[] tmp = new R4LibraryClause[m_libraryHeaders.length - 1];
@@ -285,33 +285,32 @@
* used for finding its native libraries at run time. To inspect the
* raw native library metadata refer to <tt>getLibraryClauses()</tt>.
* </p>
- * @param revision the bundle revision for the module.
* @return an array of selected library metadata objects from the manifest.
* @throws BundleException if any problems arise.
- */
- public R4Library[] getLibraries(BundleRevision revision) throws BundleException
+ **/
+ public R4Library[] getLibraries() throws BundleException
{
R4LibraryClause clause = getSelectedLibraryClause();
if (clause != null)
{
- String[] files = clause.getLibraryFiles();
- R4Library[] libraries = new R4Library[files.length];
+ String[] entries = clause.getLibraryEntries();
+ R4Library[] libraries = new R4Library[entries.length];
int current = 0;
try
{
for (int i = 0; i < libraries.length; i++)
{
- String name = getName(files[i]);
+ String name = getName(entries[i]);
boolean found = false;
for (int j = 0; !found && (j < current); j++)
{
- found = getName(files[j]).equals(name);
+ found = getName(entries[j]).equals(name);
}
if (!found)
{
libraries[current++] = new R4Library(
- m_logger, revision, clause.getLibraryFiles()[i],
+ clause.getLibraryEntries()[i],
clause.getOSNames(), clause.getProcessors(), clause.getOSVersions(),
clause.getLanguages(), clause.getSelectionFilter());
}
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java
index 2a609c4..64aae73 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4Library.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -18,15 +18,10 @@
*/
package org.apache.felix.framework.util.manifestparser;
-import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.cache.BundleRevision;
-import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
public class R4Library
{
- private Logger m_logger = null;
- private BundleRevision m_revision = null;
private String m_libraryFile = null;
private String[] m_osnames = null;
private String[] m_processors = null;
@@ -34,21 +29,21 @@
private String[] m_languages = null;
private String m_selectionFilter = null;
- public R4Library(Logger logger, BundleRevision revision,
+ public R4Library(
String libraryFile, String[] osnames, String[] processors, String[] osversions,
String[] languages, String selectionFilter) throws Exception
{
- m_logger = logger;
- m_revision = revision;
m_libraryFile = libraryFile;
m_osnames = osnames;
m_processors = processors;
m_osversions = osversions;
m_languages = languages;
m_selectionFilter = selectionFilter;
+ }
- // Make sure we actually have the library in the revision
- m_revision.findLibrary(m_libraryFile);
+ public String getEntryName()
+ {
+ return m_libraryFile;
}
public String[] getOSNames()
@@ -78,43 +73,34 @@
/**
* <p>
- * Returns a file system path to the specified library.
+ * Determines if the specified native library name matches this native
+ * library definition.
* </p>
- *
- * @param name the name of the library that is being requested.
- * @return a file system path to the specified library.
- */
- public String getPath(String name)
+ * @param name the native library name to try to match.
+ * @return <tt>true</tt> if this native library name matches this native
+ * library definition; <tt>false</tt> otherwise.
+ **/
+ public boolean match(String name)
{
String libname = System.mapLibraryName(name);
-
- try
+ if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
{
+ return true;
+ }
+ else if (libname.endsWith(".jnilib") &&
+ m_libraryFile.endsWith(".dylib"))
+ {
+ libname = libname.substring(0, libname.length() - 6) + "dylib";
if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
{
- return m_revision.findLibrary(m_libraryFile);
- }
- else if (libname.endsWith(".jnilib") &&
- m_libraryFile.endsWith(".dylib"))
- {
- libname = libname.substring(0, libname.length() - 6) + "dylib";
- if (m_libraryFile.equals(libname) || m_libraryFile.endsWith("/" + libname))
- {
- return m_revision.findLibrary(m_libraryFile);
- }
- }
- else if (m_libraryFile.equals(name) || m_libraryFile.endsWith("/" + name))
- {
- return m_revision.findLibrary(m_libraryFile);
+ return true;
}
}
- catch (Exception ex)
+ else if (m_libraryFile.equals(name) || m_libraryFile.endsWith("/" + name))
{
- m_logger.log(Logger.LOG_ERROR, "R4Library: Finding library '"
- + name + "'.", new BundleException(
- "Unable to find native library '" + name + "'."));
+ return true;
}
- return null;
+ return false;
}
public String toString()
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
index 3227620..91923db 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/R4LibraryClause.java
@@ -27,18 +27,18 @@
public class R4LibraryClause
{
- private String[] m_libraryFiles = null;
+ private String[] m_libraryEntries = null;
private String[] m_osnames = null;
private String[] m_processors = null;
private String[] m_osversions = null;
private String[] m_languages = null;
private String m_selectionFilter = null;
- public R4LibraryClause(String[] libraryFiles, String[] osnames,
+ public R4LibraryClause(String[] libraryEntries, String[] osnames,
String[] processors, String[] osversions, String[] languages,
String selectionFilter)
{
- m_libraryFiles = libraryFiles;
+ m_libraryEntries = libraryEntries;
m_osnames = osnames;
m_processors = processors;
m_osversions = osversions;
@@ -48,7 +48,7 @@
public R4LibraryClause(R4LibraryClause library)
{
- m_libraryFiles = library.m_libraryFiles;
+ m_libraryEntries = library.m_libraryEntries;
m_osnames = library.m_osnames;
m_osversions = library.m_osversions;
m_processors = library.m_processors;
@@ -56,9 +56,9 @@
m_selectionFilter = library.m_selectionFilter;
}
- public String[] getLibraryFiles()
+ public String[] getLibraryEntries()
{
- return m_libraryFiles;
+ return m_libraryEntries;
}
public String[] getOSNames()
@@ -241,7 +241,7 @@
// any number of libraries along with one set of associated
// properties.
StringTokenizer st = new StringTokenizer(s, ";");
- String[] libFiles = new String[st.countTokens()];
+ String[] libEntries = new String[st.countTokens()];
List osNameList = new ArrayList();
List osVersionList = new ArrayList();
List processorList = new ArrayList();
@@ -254,7 +254,7 @@
if (token.indexOf('=') < 0)
{
// Remove the slash, if necessary.
- libFiles[libCount] = (token.charAt(0) == '/')
+ libEntries[libCount] = (token.charAt(0) == '/')
? token.substring(1)
: token;
libCount++;
@@ -327,10 +327,10 @@
}
// Shrink lib file array.
- String[] actualLibFiles = new String[libCount];
- System.arraycopy(libFiles, 0, actualLibFiles, 0, libCount);
+ String[] actualLibEntries = new String[libCount];
+ System.arraycopy(libEntries, 0, actualLibEntries, 0, libCount);
return new R4LibraryClause(
- actualLibFiles,
+ actualLibEntries,
(String[]) osNameList.toArray(new String[osNameList.size()]),
(String[]) processorList.toArray(new String[processorList.size()]),
(String[]) osVersionList.toArray(new String[osVersionList.size()]),
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IContent.java b/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
index 4b54142..8af13eb 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
@@ -1,4 +1,4 @@
-/*
+/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
@@ -24,12 +24,38 @@
public interface IContent
{
+
+ /**
+ * <p>
+ * This method must be called before using any other methods on this
+ * interface. If the content is already opened, then subsequent calls
+ * should have no effect. This method is intended to allow the content
+ * to initialize any necessary resources.
+ * </p>
+ **/
public void open();
+
+ /**
+ * <p>
+ * This method must be called when the content is no longer needed so
+ * that any resourses being used (e.g., open files) can be closed. Once
+ * this method is called, the content is no longer usable. If the content
+ * is already closed, then calls on this method should have no effect.
+ * </p>
+ **/
public void close();
+
+ /**
+ * <p>
+ * This method determines if the specified named entry is contained in
+ * the associated content. The entry name is a relative path with '/'
+ * separators.
+ * </p>
+ * @param name The name of the entry to find.
+ * @return <tt>true</tt> if a corresponding entry was found, <tt>false</tt>
+ * otherwise.
+ **/
public boolean hasEntry(String name);
- public byte[] getEntry(String name);
- public InputStream getEntryAsStream(String name)
- throws IOException;
/**
* <p>
@@ -39,7 +65,62 @@
* for entries that represent directories should end with the '/'
* character.
* </p>
- * @ returns An enumeration of entry names or <tt>null</tt>.
- **/
+ * @returns An enumeration of entry names or <tt>null</tt>.
+ **/
public Enumeration getEntries();
-}
+
+ /**
+ * <p>
+ * This method returns the named entry as an array of bytes.
+ * </p>
+ * @param name The name of the entry to retrieve as a byte array.
+ * @return An array of bytes if the corresponding entry was found, <tt>null</tt>
+ * otherwise.
+ **/
+ public byte[] getEntryAsBytes(String name);
+
+ /**
+ * <p>
+ * This method returns the named entry as an input stream.
+ * </p>
+ * @param name The name of the entry to retrieve as an input stream.
+ * @return An input stream if the corresponding entry was found, <tt>null</tt>
+ * otherwise.
+ * @throws <tt>java.io.IOException</tt> if any error occurs.
+ **/
+ public InputStream getEntryAsStream(String name)
+ throws IOException;
+
+ /**
+ * <p>
+ * This method returns the named entry as an <tt>IContent</tt> Typically,
+ * this method only makes sense for entries that correspond to some form
+ * of aggregated resource (e.g., an embedded JAR file or directory), but
+ * implementations are free to interpret this however makes sense. This method
+ * should return a new <tt>IContent</tt> instance for every invocation and
+ * the caller is responsible for opening and closing the returned content
+ * object.
+ * </p>
+ * @param name The name of the entry to retrieve as an <tt>IContent</tt>.
+ * @return An <tt>IContent</tt> instance if a corresponding entry was found,
+ * <tt>null</tt> otherwise.
+ **/
+ public IContent getEntryAsContent(String name);
+
+ /**
+ * <p>
+ * This method returns the named entry as a file in the file system for
+ * use as a native library. It may not be possible for all content
+ * implementations (e.g., memory only) to implement this method, in which
+ * case it is acceptable to return <tt>null</tt>.
+ * </p>
+ * @param name The name of the entry to retrieve as a file.
+ * @return A string corresponding to the absolute path of the file if a
+ * corresponding entry was found, <tt>null</tt> otherwise.
+ **/
+// TODO: CACHE - This method needs to be rethought once we start allowing
+// native libs in fragments to support multi-host attachement.
+// For now, our implementations of this interface will not
+// return a new file for every invocation.
+ public String getEntryAsNativeLibrary(String name);
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/JarContent.java b/framework/src/main/java/org/apache/felix/moduleloader/JarContent.java
deleted file mode 100644
index 34cc794..0000000
--- a/framework/src/main/java/org/apache/felix/moduleloader/JarContent.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.moduleloader;
-
-import java.io.*;
-import java.util.Enumeration;
-import java.util.zip.ZipEntry;
-
-import org.apache.felix.framework.util.SecureAction;
-
-public class JarContent implements IContent
-{
- private static final int BUFSIZE = 4096;
-
- private File m_file = null;
- private JarFileX m_jarFile = null;
- private boolean m_opened = false;
-
- private static SecureAction m_secureAction = new SecureAction();
-
- public JarContent(File file)
- {
- m_file = file;
- }
-
- protected void finalize()
- {
- if (m_jarFile != null)
- {
- try
- {
- m_jarFile.close();
- }
- catch (IOException ex)
- {
- // Not much we can do, so ignore it.
- }
- }
- }
-
- public void open()
- {
- m_opened = true;
- }
-
- public synchronized void close()
- {
- try
- {
- if (m_jarFile != null)
- {
- m_jarFile.close();
- }
- }
- catch (Exception ex)
- {
- System.err.println("JarContent: " + ex);
- }
-
- m_jarFile = null;
- m_opened = false;
- }
-
- public synchronized boolean hasEntry(String name) throws IllegalStateException
- {
- if (!m_opened)
- {
- throw new IllegalStateException("JarContent is not open");
- }
-
- // Open JAR file if not already opened.
- if (m_jarFile == null)
- {
- try
- {
- openJarFile();
- }
- catch (IOException ex)
- {
- System.err.println("JarContent: " + ex);
- return false;
- }
- }
-
- try
- {
- ZipEntry ze = m_jarFile.getEntry(name);
- return ze != null;
- }
- catch (Exception ex)
- {
- return false;
- }
- finally
- {
- }
- }
-
- public synchronized byte[] getEntry(String name) throws IllegalStateException
- {
- if (!m_opened)
- {
- throw new IllegalStateException("JarContent is not open");
- }
-
- // Open JAR file if not already opened.
- if (m_jarFile == null)
- {
- try
- {
- openJarFile();
- }
- catch (IOException ex)
- {
- System.err.println("JarContent: " + ex);
- return null;
- }
- }
-
- // Get the embedded resource.
- InputStream is = null;
- ByteArrayOutputStream baos = null;
-
- try
- {
- ZipEntry ze = m_jarFile.getEntry(name);
- if (ze == null)
- {
- return null;
- }
- is = m_jarFile.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();
-
- }
- catch (Exception ex)
- {
- return null;
- }
- finally
- {
- try
- {
- if (baos != null) baos.close();
- }
- catch (Exception ex)
- {
- }
- try
- {
- if (is != null) is.close();
- }
- catch (Exception ex)
- {
- }
- }
- }
-
- public synchronized InputStream getEntryAsStream(String name)
- throws IllegalStateException, IOException
- {
- if (!m_opened)
- {
- throw new IllegalStateException("JarContent is not open");
- }
-
- // Open JAR file if not already opened.
- if (m_jarFile == null)
- {
- try
- {
- openJarFile();
- }
- catch (IOException ex)
- {
- System.err.println("JarContent: " + ex);
- return null;
- }
- }
-
- // Get the embedded resource.
- InputStream is = null;
-
- try
- {
- ZipEntry ze = m_jarFile.getEntry(name);
- if (ze == null)
- {
- return null;
- }
- is = m_jarFile.getInputStream(ze);
- if (is == null)
- {
- return null;
- }
- }
- catch (Exception ex)
- {
- return null;
- }
-
- return is;
- }
-
- public synchronized Enumeration getEntries()
- {
- if (!m_opened)
- {
- throw new IllegalStateException("JarContent is not open");
- }
-
- // Open JAR file if not already opened.
- if (m_jarFile == null)
- {
- try
- {
- openJarFile();
- }
- catch (IOException ex)
- {
- System.err.println("JarContent: " + ex);
- return null;
- }
- }
-
- // Wrap entries enumeration to filter non-matching entries.
- Enumeration e = new EntriesEnumeration(m_jarFile.entries());
-
- // Spec says to return null if there are no entries.
- return (e.hasMoreElements()) ? e : null;
- }
-
- private void openJarFile() throws IOException
- {
- if (m_jarFile == null)
- {
- m_jarFile = m_secureAction.openJAR(m_file);
- }
- }
-
- public String toString()
- {
- return "JAR " + m_file.getPath();
- }
-
- public File getFile()
- {
- return m_file;
- }
-
- private static class EntriesEnumeration implements Enumeration
- {
- private Enumeration m_enumeration = null;
-
- public EntriesEnumeration(Enumeration enumeration)
- {
- m_enumeration = enumeration;
- }
-
- public boolean hasMoreElements()
- {
- return m_enumeration.hasMoreElements();
- }
-
- public Object nextElement()
- {
- return ((ZipEntry) m_enumeration.nextElement()).getName();
- }
- }
-}
\ No newline at end of file