You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2009/07/13 18:15:18 UTC

svn commit: r793616 - in /felix/trunk/framework/src/main/java/org/apache/felix: framework/ModuleImpl.java framework/cache/DirectoryContent.java framework/cache/JarContent.java moduleloader/IContent.java

Author: rickhall
Date: Mon Jul 13 16:15:18 2009
New Revision: 793616

URL: http://svn.apache.org/viewvc?rev=793616&view=rev
Log:
Modify cache so it makes a copy each time a native library is requested.
This is necessary for fragments containing native libraries, since they
can attach to multiple hosts. (FELIX-1134)

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
    felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IContent.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java?rev=793616&r1=793615&r2=793616&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java Mon Jul 13 16:15:18 2009
@@ -219,7 +219,7 @@
                 String entryName = m_nativeLibraries[i].getEntryName();
                 if (entryName != null)
                 {
-                    if (m_content.getEntryAsNativeLibrary(entryName) == null)
+                    if (!m_content.hasEntry(entryName))
                     {
                         throw new BundleException("Native library does not exist: " + entryName);
                     }
@@ -1567,6 +1567,9 @@
     public class ModuleClassLoader extends SecureClassLoader implements BundleReference
     {
         private final Map m_jarContentToDexFile;
+        private Object[][] m_libs = new Object[0][];
+        private static final int LIBNAME_IDX = 0;
+        private static final int LIBPATH_IDX = 1;
 
         public ModuleClassLoader(ClassLoader parent)
         {
@@ -1882,16 +1885,46 @@
                 name = name.substring(1);
             }
 
-            R4Library[] libs = getNativeLibraries();
-            for (int i = 0; (libs != null) && (i < libs.length); i++)
+            String result = null;
+            // CONCURRENCY: In the long run, we might want to break this
+            // sync block in two to avoid manipulating the cache while
+            // holding the lock, but for now we will do it the simple way.
+            synchronized (this)
             {
-                if (libs[i].match(m_configMap, name))
+                // Check to make sure we haven't already found this library.
+                for (int i = 0; (result == null) && (i < m_libs.length); i++)
                 {
-                    return getContent().getEntryAsNativeLibrary(libs[i].getEntryName());
+                    if (m_libs[i][LIBNAME_IDX].equals(name))
+                    {
+                        result = (String) m_libs[i][LIBPATH_IDX];
+                    }
+                }
+
+                // If we don't have a cached result, see if we have a matching
+                // native library.
+                if (result == null)
+                {
+                    R4Library[] libs = getNativeLibraries();
+                    for (int i = 0; (libs != null) && (i < libs.length); i++)
+                    {
+                        if (libs[i].match(m_configMap, name))
+                        {
+                            result = getContent().getEntryAsNativeLibrary(libs[i].getEntryName());
+                        }
+                    }
+
+                    // Remember the result for future requests.
+                    if (result != null)
+                    {
+                        Object[][] tmp = new Object[m_libs.length + 1][];
+                        System.arraycopy(m_libs, 0, tmp, 0, m_libs.length);
+                        tmp[m_libs.length] = new Object[] { name, result };
+                        m_libs = tmp;
+                    }
                 }
             }
 
-            return null;
+            return result;
         }
 
         public String toString()

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java?rev=793616&r1=793615&r2=793616&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/DirectoryContent.java Mon Jul 13 16:15:18 2009
@@ -23,18 +23,21 @@
 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.osgi.framework.Constants;
 
 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 static final transient String LIBRARY_DIRECTORY = "-lib";
 
     private final Logger m_logger;
     private final Map m_configMap;
     private final Object m_revisionLock;
     private final File m_rootDir;
     private final File m_dir;
+    private int m_libCount = 0;
 
     public DirectoryContent(Logger logger, Map configMap, Object revisionLock,
         File rootDir, File dir)
@@ -155,15 +158,15 @@
         else if (BundleCache.getSecureAction().fileExists(file)
             && entryName.endsWith(".jar"))
         {
-            File extractedDir = new File(embedDir,
+            File extractDir = new File(embedDir,
                 (entryName.lastIndexOf('/') >= 0)
                     ? entryName.substring(0, entryName.lastIndexOf('/'))
                     : entryName);
             synchronized (m_revisionLock)
             {
-                if (!BundleCache.getSecureAction().fileExists(extractedDir))
+                if (!BundleCache.getSecureAction().fileExists(extractDir))
                 {
-                    if (!BundleCache.getSecureAction().mkdirs(extractedDir))
+                    if (!BundleCache.getSecureAction().mkdirs(extractDir))
                     {
                         m_logger.log(
                             Logger.LOG_ERROR,
@@ -171,7 +174,7 @@
                     }
                 }
             }
-            return new JarContent(m_logger, m_configMap, m_revisionLock, extractedDir, file);
+            return new JarContent(m_logger, m_configMap, m_revisionLock, extractDir, file);
         }
 
         // The entry could not be found, so return null.
@@ -179,9 +182,110 @@
     }
 
 // TODO: This will need to consider security.
-    public synchronized String getEntryAsNativeLibrary(String name)
+    public synchronized String getEntryAsNativeLibrary(String entryName)
     {
-        return BundleCache.getSecureAction().getAbsolutePath(new File(m_rootDir, name));
+        // Return result.
+        String result = null;
+
+        // 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;
+
+        // Any embedded native library files will be extracted to the lib directory.
+        File libDir = new File(m_rootDir, m_dir.getName() + LIBRARY_DIRECTORY);
+
+        // The entry must exist and refer to a file, not a directory,
+        // since we are expecting it to be a native library.
+        File entryFile = new File(m_dir, entryName);
+        if (BundleCache.getSecureAction().fileExists(entryFile)
+            && !BundleCache.getSecureAction().isFileDirectory(entryFile))
+        {
+            // Extracting the embedded native library 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)
+            {
+                // Since native libraries cannot be shared, we must extract a
+                // separate copy per request, so use the request library counter
+                // as part of the extracted path.
+                File libFile = new File(
+                    libDir, Integer.toString(m_libCount) + File.separatorChar + entryName);
+                // Increment library request counter.
+                m_libCount++;
+
+                if (!BundleCache.getSecureAction().fileExists(libFile))
+                {
+                    if (!BundleCache.getSecureAction().fileExists(libFile.getParentFile()))
+                    {
+                        if (!BundleCache.getSecureAction().mkdirs(libFile.getParentFile()))
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Unable to create library directory.");
+                        }
+                        else
+                        {
+                            InputStream is = null;
+
+                            try
+                            {
+                                is = new BufferedInputStream(
+                                    new FileInputStream(entryFile),
+                                    BundleCache.BUFSIZE);
+                                if (is == null)
+                                {
+                                    throw new IOException("No input stream: " + entryName);
+                                }
+
+                                // Create the file.
+                                BundleCache.copyStreamToFile(is, libFile);
+
+                                // Perform exec permission command on extracted library
+                                // if one is configured.
+                                String command = (String) m_configMap.get(
+                                    Constants.FRAMEWORK_EXECPERMISSION);
+                                if (command != null)
+                                {
+                                    Properties props = new Properties();
+                                    props.setProperty("abspath", libFile.toString());
+                                    command = Util.substVars(command, "command", null, props);
+                                    Process p = BundleCache.getSecureAction().exec(command);
+                                    p.waitFor();
+                                }
+
+                                // Return the path to the extracted native library.
+                                result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                            }
+                            catch (Exception ex)
+                            {
+                                m_logger.log(
+                                    Logger.LOG_ERROR,
+                                    "Extracting native library.", ex);
+                            }
+                            finally
+                            {
+                                try
+                                {
+                                    if (is != null) is.close();
+                                }
+                                catch (IOException ex)
+                                {
+                                    // Not much we can do.
+                                }
+                            }
+                        }
+                    }
+                }
+                else
+                {
+                    // Return the path to the extracted native library.
+                    result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                }
+            }
+        }
+
+        return result;
     }
 
     public String toString()

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java?rev=793616&r1=793615&r2=793616&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/JarContent.java Mon Jul 13 16:15:18 2009
@@ -38,7 +38,7 @@
 {
     private static final int BUFSIZE = 4096;
     private static final transient String EMBEDDED_DIRECTORY = "-embedded";
-    private static final transient String LIBRARY_DIRECTORY = "lib";
+    private static final transient String LIBRARY_DIRECTORY = "-lib";
 
     private final Logger m_logger;
     private final Map m_configMap;
@@ -46,6 +46,7 @@
     private final File m_rootDir;
     private final File m_file;
     private JarFileX m_jarFile = null;
+    private int m_libCount = 0;
 
     public JarContent(Logger logger, Map configMap, Object revisionLock, File rootDir, File file)
     {
@@ -303,7 +304,7 @@
         ZipEntry ze = m_jarFile.getEntry(entryName);
         if ((ze != null) && ze.isDirectory())
         {
-            File extractedDir = new File(embedDir, entryName);
+            File extractDir = new File(embedDir, entryName);
 
             // Extracting an embedded directory file impacts all other existing
             // contents for this revision, so we have to grab the revision
@@ -311,9 +312,9 @@
             // directory to avoid a race condition.
             synchronized (m_revisionLock)
             {
-                if (!BundleCache.getSecureAction().fileExists(extractedDir))
+                if (!BundleCache.getSecureAction().fileExists(extractDir))
                 {
-                    if (!BundleCache.getSecureAction().mkdirs(extractedDir))
+                    if (!BundleCache.getSecureAction().mkdirs(extractDir))
                     {
                         m_logger.log(
                             Logger.LOG_ERROR,
@@ -325,7 +326,7 @@
         }
         else if ((ze != null) && ze.getName().endsWith(".jar"))
         {
-            File extractedJar = new File(embedDir, entryName);
+            File extractJar = new File(embedDir, entryName);
 
             // Extracting the embedded JAR file impacts all other existing
             // contents for this revision, so we have to grab the revision
@@ -333,7 +334,7 @@
             // to avoid a race condition.
             synchronized (m_revisionLock)
             {
-                if (!BundleCache.getSecureAction().fileExists(extractedJar))
+                if (!BundleCache.getSecureAction().fileExists(extractJar))
                 {
                     try
                     {
@@ -349,7 +350,7 @@
             }
             return new JarContent(
                 m_logger, m_configMap, m_revisionLock,
-                extractedJar.getParentFile(), extractedJar);
+                extractJar.getParentFile(), extractJar);
         }
 
         // The entry could not be found, so return null.
@@ -357,8 +358,11 @@
     }
 
 // TODO: This will need to consider security.
-    public synchronized String getEntryAsNativeLibrary(String name)
+    public synchronized String getEntryAsNativeLibrary(String entryName)
     {
+        // Return result.
+        String result = null;
+
         // Open JAR file if not already opened.
         if (m_jarFile == null)
         {
@@ -370,83 +374,111 @@
             {
                 m_logger.log(
                     Logger.LOG_ERROR,
-                    "JarContent: Unable to open JAR file.", ex);
+                    "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);
-                }
+        // Remove any leading slash.
+        entryName = (entryName.startsWith("/")) ? entryName.substring(1) : entryName;
 
-                // Create the file.
-                BundleCache.copyStreamToFile(is, libFile);
+        // Any embedded native libraries will be extracted to the lib directory.
+        // Since embedded library file names may clash when extracting from multiple
+        // embedded JAR files, the embedded lib directory is per embedded JAR file.
+        File libDir = new File(m_rootDir, m_file.getName() + LIBRARY_DIRECTORY);
 
-                // Perform exec permission command on extracted library
-                // if one is configured.
-                String command = (String) m_configMap.get(Constants.FRAMEWORK_EXECPERMISSION);
-                if (command != null)
-                {
-                    Properties props = new Properties();
-                    props.setProperty("abspath", libFile.toString());
-                    command = Util.substVars(command, "command", null, props);
-                    Process p = BundleCache.getSecureAction().exec(command);
-                    p.waitFor();
-                }
-            }
-            catch (Exception ex)
-            {
-                m_logger.log(
-                    Logger.LOG_ERROR,
-                    "JarContent: Extracting native library.", ex);
-            }
-            finally
+        // The entry name must refer to a file type, since it is
+        // a native library, not a directory.
+        ZipEntry ze = m_jarFile.getEntry(entryName);
+        if ((ze != null) && !ze.isDirectory())
+        {
+            // Extracting the embedded native library 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)
             {
-                try
+                // Since native libraries cannot be shared, we must extract a
+                // separate copy per request, so use the request library counter
+                // as part of the extracted path.
+                File libFile = new File(
+                    libDir, Integer.toString(m_libCount) + File.separatorChar + entryName);
+                // Increment library request counter.
+                m_libCount++;
+
+                if (!BundleCache.getSecureAction().fileExists(libFile))
                 {
-                    if (is != null) is.close();
+                    if (!BundleCache.getSecureAction().fileExists(libFile.getParentFile()))
+                    {
+                        if (!BundleCache.getSecureAction().mkdirs(libFile.getParentFile()))
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Unable to create library directory.");
+                        }
+                        else
+                        {
+                            InputStream is = null;
+
+                            try
+                            {
+                                is = new BufferedInputStream(
+                                    m_jarFile.getInputStream(ze),
+                                    BundleCache.BUFSIZE);
+                                if (is == null)
+                                {
+                                    throw new IOException("No input stream: " + entryName);
+                                }
+
+                                // Create the file.
+                                BundleCache.copyStreamToFile(is, libFile);
+
+                                // Perform exec permission command on extracted library
+                                // if one is configured.
+                                String command = (String) m_configMap.get(
+                                    Constants.FRAMEWORK_EXECPERMISSION);
+                                if (command != null)
+                                {
+                                    Properties props = new Properties();
+                                    props.setProperty("abspath", libFile.toString());
+                                    command = Util.substVars(command, "command", null, props);
+                                    Process p = BundleCache.getSecureAction().exec(command);
+                                    p.waitFor();
+                                }
+
+                                // Return the path to the extracted native library.
+                                result = BundleCache.getSecureAction().getAbsolutePath(libFile);
+                            }
+                            catch (Exception ex)
+                            {
+                                m_logger.log(
+                                    Logger.LOG_ERROR,
+                                    "Extracting native library.", ex);
+                            }
+                            finally
+                            {
+                                try
+                                {
+                                    if (is != null) is.close();
+                                }
+                                catch (IOException ex)
+                                {
+                                    // Not much we can do.
+                                }
+                            }
+                        }
+                    }
                 }
-                catch (IOException ex)
+                else
                 {
-                    // Not much we can do.
+                    // Return the path to the extracted native library.
+                    result = BundleCache.getSecureAction().getAbsolutePath(libFile);
                 }
             }
         }
 
-        return BundleCache.getSecureAction().getAbsolutePath(libFile);
+        return result;
     }
 
     public String toString()

Modified: felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IContent.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IContent.java?rev=793616&r1=793615&r2=793616&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IContent.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/moduleloader/IContent.java Mon Jul 13 16:15:18 2009
@@ -32,7 +32,7 @@
      * is already closed, then calls on this method should have no effect.
      * </p>
     **/
-    public void close();
+    void close();
 
     /**
      * <p>
@@ -44,7 +44,7 @@
      * @return <tt>true</tt> if a corresponding entry was found, <tt>false</tt>
      *         otherwise.
     **/
-    public boolean hasEntry(String name);
+    boolean hasEntry(String name);
 
     /**
      * <p>
@@ -56,7 +56,7 @@
      * </p>
      * @returns An enumeration of entry names or <tt>null</tt>.
     **/
-    public Enumeration getEntries();
+    Enumeration getEntries();
 
     /**
      * <p>
@@ -66,7 +66,7 @@
      * @return An array of bytes if the corresponding entry was found, <tt>null</tt>
      *         otherwise.
     **/
-    public byte[] getEntryAsBytes(String name);
+    byte[] getEntryAsBytes(String name);
 
     /**
      * <p>
@@ -77,8 +77,7 @@
      *         otherwise.
      * @throws <tt>java.io.IOException</tt> if any error occurs.
     **/
-    public InputStream getEntryAsStream(String name)
-        throws IOException;
+    InputStream getEntryAsStream(String name) throws IOException;
 
     /**
      * <p>
@@ -94,22 +93,20 @@
      * @return An <tt>IContent</tt> instance if a corresponding entry was found,
      *         <tt>null</tt> otherwise.
     **/
-    public IContent getEntryAsContent(String name);
+    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>.
+     * case it is acceptable to return <tt>null</tt>. Since native libraries
+     * can only be associated with a single class loader, this method should
+     * return a unique file per request.
      * </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);
+    String getEntryAsNativeLibrary(String name);
 }
\ No newline at end of file