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 2010/10/27 20:52:09 UTC

svn commit: r1028068 - in /felix/trunk/framework/src/main/java/org/apache/felix/framework: BundleImpl.java Felix.java URLHandlersBundleURLConnection.java cache/BundleArchive.java

Author: rickhall
Date: Wed Oct 27 18:52:09 2010
New Revision: 1028068

URL: http://svn.apache.org/viewvc?rev=1028068&view=rev
Log:
Do not reset revision number to allow resource URLs to survive
framework refreshes. (FELIX-2560)

Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java?rev=1028068&r1=1028067&r2=1028068&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/BundleImpl.java Wed Oct 27 18:52:09 2010
@@ -1075,7 +1075,7 @@ class BundleImpl implements Bundle
         throws Exception
     {
         // This operation will increase the revision count for the bundle.
-        m_archive.revise(false, location, is);
+        m_archive.revise(location, is);
         try
         {
             Module module = createModule();
@@ -1135,19 +1135,18 @@ class BundleImpl implements Bundle
     {
         // Get and parse the manifest from the most recent revision to
         // create an associated module for it.
-        Map headerMap = m_archive.getRevision(
-            m_archive.getRevisionCount() - 1).getManifestHeader();
+        Map headerMap = m_archive.getCurrentRevision().getManifestHeader();
 
         // Create the module instance.
-        final int revision = m_archive.getRevisionCount() - 1;
         ModuleImpl module = new ModuleImpl(
             getFramework().getLogger(),
             getFramework().getConfig(),
             getFramework().getResolver(),
             this,
-            Long.toString(getBundleId()) + "." + Integer.toString(revision),
+            Long.toString(getBundleId())
+                + "." + m_archive.getCurrentRevisionNumber().toString(),
             headerMap,
-            m_archive.getRevision(revision).getContent(),
+            m_archive.getCurrentRevision().getContent(),
             getFramework().getBundleStreamHandler(),
             getFramework().getBootPackages(),
             getFramework().getBootPackageWildcards());

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=1028068&r1=1028067&r2=1028068&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Wed Oct 27 18:52:09 2010
@@ -2491,10 +2491,7 @@ public class Felix extends BundleImpl im
                 // due to an error or system crash.
                 try
                 {
-                    if (ba.getRevisionCount() > 1)
-                    {
-                        ba.purge();
-                    }
+                    ba.purge();
                 }
                 catch (Exception ex)
                 {

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java?rev=1028068&r1=1028067&r2=1028068&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/URLHandlersBundleURLConnection.java Wed Oct 27 18:52:09 2010
@@ -77,17 +77,27 @@ class URLHandlersBundleURLConnection ext
         }
         m_contentTime = bundle.getLastModified();
 
-        int revision = Util.getModuleRevisionFromModuleId(url.getHost());
+        // Get the bundle's modules to find the target module.
         List<Module> modules = bundle.getModules();
-        if ((modules == null) || (revision >= modules.size()))
+        if ((modules == null) || modules.isEmpty())
         {
             throw new IOException("Resource does not exist: " + url);
         }
 
-        // If the revision is not specified, check the latest
-        if (revision < 0)
+        // Search for matching module name.
+        for (Module m : modules)
         {
-            revision = modules.size() - 1;
+            if (m.getId().equals(url.getHost()))
+            {
+                m_targetModule = m;
+                break;
+            }
+        }
+
+        // If not found, assume the current module.
+        if (m_targetModule == null)
+        {
+            m_targetModule = modules.get(modules.size() - 1);
         }
 
         // If the resource cannot be found at the current class path index,
@@ -97,15 +107,14 @@ class URLHandlersBundleURLConnection ext
         // Of course, this approach won't work in cases where there are multiple
         // resources with the same path, since it will always find the first
         // one on the class path.
-        m_targetModule = modules.get(revision);
         m_classPathIdx = url.getPort();
         if (m_classPathIdx < 0)
         {
             m_classPathIdx = 0;
         }
-        if (!modules.get(revision).hasInputStream(m_classPathIdx, url.getPath()))
+        if (!m_targetModule.hasInputStream(m_classPathIdx, url.getPath()))
         {
-            URL newurl = modules.get(revision).getResourceByDelegation(url.getPath());
+            URL newurl = m_targetModule.getResourceByDelegation(url.getPath());
             if (newurl == null)
             {
                 throw new IOException("Resource does not exist: " + url);

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java?rev=1028068&r1=1028067&r2=1028068&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java Wed Oct 27 18:52:09 2010
@@ -21,6 +21,8 @@ package org.apache.felix.framework.cache
 import java.io.*;
 
 import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import org.apache.felix.framework.Logger;
 import org.osgi.framework.Bundle;
 
@@ -97,23 +99,9 @@ public class BundleArchive
     **/
     private long m_refreshCount = -1;
 
-    private BundleRevision[] m_revisions = null;
-
-    /**
-     * <p>
-     * This constructor is only used by the system bundle archive implementation
-     * because it is special an is not really an archive.
-     * </p>
-    **/
-    public BundleArchive()
-    {
-        m_logger = null;
-        m_configMap = null;
-        m_archiveRootDir = null;
-
-        String s = (String) m_configMap.get(BundleCache.CACHE_SINGLEBUNDLEFILE_PROP);
-        m_isSingleBundleFile = (s == null) || (!s.equalsIgnoreCase("true")) ? false : true;
-    }
+    // Maps a Long revision number to a BundleRevision.
+    private final SortedMap<Long, BundleRevision> m_revisions
+        = new TreeMap<Long, BundleRevision>();
 
     /**
      * <p>
@@ -157,7 +145,7 @@ public class BundleArchive
         initialize();
 
         // Add a revision for the content.
-        revise(false, m_originalLocation, is);
+        reviseInternal(false, new Long(0), m_originalLocation, is);
     }
 
     /**
@@ -187,44 +175,48 @@ public class BundleArchive
             readBundleInfo();
         }
 
-        // Add a revision for each one that already exists in the file
-        // system. The file system might contain more than one revision
-        // if the bundle was updated in a previous session, but the
-        // framework was not refreshed; this might happen if the framework
-        // did not exit cleanly. We must create the existing revisions so
-        // that they can be properly purged.
-        int revisionCount = 0;
-        while (true)
-        {
-            // Count the number of existing revision directories, which
-            // will be in a directory named like:
-            //     "${REVISION_DIRECTORY)${refresh-count}.${revision-count}"
-            File revisionRootDir = new File(m_archiveRootDir,
-                REVISION_DIRECTORY + getRefreshCount() + "." + revisionCount);
-            if (!BundleCache.getSecureAction().fileExists(revisionRootDir))
-            {
-                break;
-            }
-
-            // Increment the revision count.
-            revisionCount++;
-        }
-
-        // If there are multiple revisions in the file system, then create
-        // an array that is big enough to hold all revisions minus one; the
-        // call below to revise() will add the most recent revision. NOTE: We
-        // do not actually need to add a real revision object for the older
-        // revisions since they will be purged immediately on framework startup.
-        if (revisionCount > 1)
-        {
-            m_revisions = new BundleRevision[revisionCount - 1];
-        }
-
-        // Add the revision object for the most recent revision. We first try
-        // to read the location from the current revision - if that fails we
-        // likely have an old bundle cache and read the location the old way.
-        // The next revision will update the bundle cache.
-        revise(true, getRevisionLocation(revisionCount - 1), null);
+        // Add a revision number for each revision that exists in the file
+        // system. The file system might contain more than one revision if
+        // the bundle was updated in a previous session, but the framework
+        // was not refreshed; this might happen if the framework did not
+        // exit cleanly. We must add the existing revisions so that
+        // they can be properly purged.
+
+        // Find the existing revision directories, which will be named like:
+        //     "${REVISION_DIRECTORY)${refresh-count}.${revision-number}"
+        File[] children = m_archiveRootDir.listFiles();
+        for (File child : children)
+        {
+            if (child.getName().startsWith(REVISION_DIRECTORY)
+                && child.isDirectory())
+            {
+                // Determine the revision number and add it to the revision map.
+                int idx = child.getName().lastIndexOf('.');
+                if (idx > 0)
+                {
+                    Long revNum = Long.decode(child.getName().substring(idx + 1));
+                    m_revisions.put(revNum, null);
+                }
+            }
+        }
+
+        if (m_revisions.isEmpty())
+        {
+            throw new Exception(
+                "No valid revisions in bundle archive directory: "
+                + archiveRootDir);
+        }
+
+        // Remove the last revision number since the call to reviseInternal()
+        // will properly add the most recent bundle revision.
+        // NOTE: We do not actually need to add a real revision object for the
+        // older revisions since they will be purged immediately on framework
+        // startup.
+        Long currentRevNum = m_revisions.lastKey();
+        m_revisions.remove(currentRevNum);
+
+        // Add the revision object for the most recent revision.
+        reviseInternal(true, currentRevNum, getRevisionLocation(currentRevNum), null);
     }
 
     /**
@@ -478,13 +470,24 @@ public class BundleArchive
 
     /**
      * <p>
-     * Returns the number of revisions available for this archive.
+     * Returns the current revision object for the archive.
+     * </p>
+     * @return the current revision object for the archive.
+    **/
+    public synchronized Long getCurrentRevisionNumber()
+    {
+        return (m_revisions.isEmpty()) ? null : m_revisions.lastKey();
+    }
+
+    /**
+     * <p>
+     * Returns the current revision object for the archive.
      * </p>
-     * @return tthe number of revisions available for this archive.
+     * @return the current revision object for the archive.
     **/
-    public synchronized int getRevisionCount()
+    public synchronized BundleRevision getCurrentRevision()
     {
-        return (m_revisions == null) ? 0 : m_revisions.length;
+        return (m_revisions.isEmpty()) ? null : m_revisions.get(m_revisions.lastKey());
     }
 
     /**
@@ -493,24 +496,44 @@ public class BundleArchive
      * </p>
      * @return the revision object for the specified revision.
     **/
-    public synchronized BundleRevision getRevision(int i)
+    public synchronized BundleRevision getRevision(Long l)
     {
-        if ((i >= 0) && (i < getRevisionCount()))
-        {
-            return m_revisions[i];
-        }
-        return null;
+        return m_revisions.get(l);
     }
 
     /**
      * <p>
-     * This method adds a revision to the archive. The revision is created
-     * based on the specified location and/or input stream.
+     * This method adds a revision to the archive using the associated
+     * location and input stream. If the input stream is null, then the
+     * location is used a URL to obtain an input stream.
      * </p>
      * @param location the location string associated with the revision.
+     * @param is the input stream from which to read the revision.
      * @throws Exception if any error occurs.
     **/
-    public synchronized void revise(boolean isReload, String location, InputStream is)
+    public synchronized void revise(String location, InputStream is)
+        throws Exception
+    {
+        Long revNum = (m_revisions.isEmpty())
+            ? new Long(0)
+            : new Long(m_revisions.lastKey().longValue() + 1);
+
+        reviseInternal(false, revNum, location, is);
+    }
+
+    /**
+     * Actually adds a revision to the bundle archive. This method is also
+     * used to reload cached bundles too. The revision is given the specified
+     * revision number and is read from the input stream if supplied or from
+     * the location URL if not.
+     * @param isReload if the bundle is being reloaded or not.
+     * @param revNum the revision number of the revision.
+     * @param location the location associated with the revision.
+     * @param is the input stream from which to read the revision.
+     * @throws Exception if any error occurs.
+     */
+    private void reviseInternal(
+        boolean isReload, Long revNum, String location, InputStream is)
         throws Exception
     {
         // If we have an input stream, then we have to use it
@@ -521,7 +544,9 @@ public class BundleArchive
         {
             location = "inputstream:";
         }
-        BundleRevision revision = createRevisionFromLocation(location, is);
+
+        // Create a bundle revision for revision number.
+        BundleRevision revision = createRevisionFromLocation(location, is, revNum);
         if (revision == null)
         {
             throw new Exception("Unable to revise archive.");
@@ -529,21 +554,11 @@ public class BundleArchive
 
         if (!isReload)
         {
-            setRevisionLocation(location, (m_revisions == null) ? 0 : m_revisions.length);
+            setRevisionLocation(location, revNum);
         }
 
-        // Add new revision to revision array.
-        if (m_revisions == null)
-        {
-            m_revisions = new BundleRevision[] { revision };
-        }
-        else
-        {
-            BundleRevision[] tmp = new BundleRevision[m_revisions.length + 1];
-            System.arraycopy(m_revisions, 0, tmp, 0, m_revisions.length);
-            tmp[m_revisions.length] = revision;
-            m_revisions = tmp;
-        }
+        // Add new revision to revision map.
+        m_revisions.put(revNum, revision);
     }
 
     /**
@@ -561,14 +576,17 @@ public class BundleArchive
     public synchronized boolean rollbackRevise() throws Exception
     {
         // Can only undo the revision if there is more than one.
-        if (getRevisionCount() <= 1)
+        if (m_revisions.size() <= 1)
         {
             return false;
         }
 
+        Long revNum = m_revisions.lastKey();
+        BundleRevision revision = m_revisions.remove(revNum);
+
         try
         {
-            m_revisions[m_revisions.length - 1].close();
+            revision.close();
         }
         catch(Exception ex)
         {
@@ -577,21 +595,17 @@ public class BundleArchive
         }
 
         File revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY +
-            getRefreshCount() + "." + (m_revisions.length - 1));
+            getRefreshCount() + "." + revNum.toString());
 
         if (BundleCache.getSecureAction().fileExists(revisionDir))
         {
             BundleCache.deleteDirectoryTree(revisionDir);
         }
 
-        BundleRevision[] tmp = new BundleRevision[m_revisions.length - 1];
-        System.arraycopy(m_revisions, 0, tmp, 0, m_revisions.length - 1);
-        m_revisions = tmp;
-
         return true;
     }
 
-    private synchronized String getRevisionLocation(int revision) throws Exception
+    private synchronized String getRevisionLocation(Long revNum) throws Exception
     {
         InputStream is = null;
         BufferedReader br = null;
@@ -599,7 +613,7 @@ public class BundleArchive
         {
             is = BundleCache.getSecureAction().getFileInputStream(new File(
                 new File(m_archiveRootDir, REVISION_DIRECTORY +
-                getRefreshCount() + "." + revision), REVISION_LOCATION_FILE));
+                getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
 
             br = new BufferedReader(new InputStreamReader(is));
             return br.readLine();
@@ -611,7 +625,8 @@ public class BundleArchive
         }
     }
 
-    private synchronized void setRevisionLocation(String location, int revision) throws Exception
+    private synchronized void setRevisionLocation(String location, Long revNum)
+        throws Exception
     {
         // Save current revision location.
         OutputStream os = null;
@@ -621,7 +636,7 @@ public class BundleArchive
             os = BundleCache.getSecureAction()
                 .getFileOutputStream(new File(
                     new File(m_archiveRootDir, REVISION_DIRECTORY +
-                    getRefreshCount() + "." + revision), REVISION_LOCATION_FILE));
+                    getRefreshCount() + "." + revNum.toString()), REVISION_LOCATION_FILE));
             bw = new BufferedWriter(new OutputStreamWriter(os));
             bw.write(location, 0, location.length());
         }
@@ -635,25 +650,24 @@ public class BundleArchive
     public synchronized void close()
     {
         // Get the current revision count.
-        int count = getRevisionCount();
-        for (int i = 0; i < count; i++)
+        for (BundleRevision revision : m_revisions.values())
         {
             // Dispose of the revision, but this might be null in certain
             // circumstances, such as if this bundle archive was created
             // for an existing bundle that was updated, but not refreshed
             // due to a system crash; see the constructor code for details.
-            if (m_revisions[i] != null)
+            if (revision != null)
             {
                 try
                 {
-                    m_revisions[i].close();
+                    revision.close();
                 }
                 catch (Exception ex)
                 {
                     m_logger.log(
                         Logger.LOG_ERROR,
                             "Unable to close revision - "
-                            + m_revisions[i].getRevisionRootDir(), ex);
+                            + revision.getRevisionRootDir(), ex);
                 }
             }
         }
@@ -686,41 +700,50 @@ public class BundleArchive
     **/
     public synchronized void purge() throws Exception
     {
-        // Close the revisions and then delete all but the current revision.
-        // We don't delete it the current revision, because we want to rename it
-        // to the new refresh level.
-        close();
-        long refreshCount = getRefreshCount();
-        int count = getRevisionCount();
-        File revisionDir = null;
-        for (int i = 0; i < count - 1; i++)
+        if (m_revisions.size() > 1)
         {
-            revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY + refreshCount + "." + i);
-            if (BundleCache.getSecureAction().fileExists(revisionDir))
-            {
-                BundleCache.deleteDirectoryTree(revisionDir);
+            // Close the revisions and then delete all but the current revision.
+            // We don't delete it the current revision, because we want to rename it
+            // to the new refresh level.
+            close();
+
+            // Remove the current revision from the revision map so it doesn't
+            // get deleted.
+            Long currentRevNum = m_revisions.lastKey();
+            m_revisions.remove(currentRevNum);
+
+            // Delete all old revisions.
+            long refreshCount = getRefreshCount();
+            for (Long revNum : m_revisions.keySet())
+            {
+                File revisionDir = new File(
+                    m_archiveRootDir,
+                    REVISION_DIRECTORY + refreshCount + "." + revNum.toString());
+                if (BundleCache.getSecureAction().fileExists(revisionDir))
+                {
+                    BundleCache.deleteDirectoryTree(revisionDir);
+                }
             }
-        }
 
-        // Save the current revision location for use later when
-        // we recreate the revision.
-        String location = getRevisionLocation(count -1);
+            // Increment the refresh count.
+            setRefreshCount(refreshCount + 1);
 
-        // Increment the refresh count.
-        setRefreshCount(refreshCount + 1);
-
-        // Rename the current revision directory to be the zero revision
-        // of the new refresh level.
-        File currentDir = new File(m_archiveRootDir, REVISION_DIRECTORY + (refreshCount + 1) + ".0");
-        revisionDir = new File(m_archiveRootDir, REVISION_DIRECTORY + refreshCount + "." + (count - 1));
-        BundleCache.getSecureAction().renameFile(revisionDir, currentDir);
-
-        // Null the revision array since they are all invalid now.
-        m_revisions = null;
-        // Finally, recreate the revision for the current location.
-        BundleRevision revision = createRevisionFromLocation(location, null);
-        // Create new revision array.
-        m_revisions = new BundleRevision[] { revision };
+            // Rename the current revision directory to the new refresh level.
+            File currentDir = new File(m_archiveRootDir,
+                REVISION_DIRECTORY + (refreshCount + 1) + "." + currentRevNum.toString());
+            File revisionDir = new File(m_archiveRootDir,
+                REVISION_DIRECTORY + refreshCount + "." + currentRevNum.toString());
+            BundleCache.getSecureAction().renameFile(revisionDir, currentDir);
+
+            // Clear the revision map since they are all invalid now.
+            m_revisions.clear();
+
+            // Recreate the revision for the current location.
+            BundleRevision revision = createRevisionFromLocation(
+                getRevisionLocation(currentRevNum), null, currentRevNum);
+            // Add new revision to the revision map.
+            m_revisions.put(currentRevNum, revision);
+        }
     }
 
     /**
@@ -778,19 +801,21 @@ public class BundleArchive
      * </p>
      * @return the location string associated with this archive.
     **/
-    private BundleRevision createRevisionFromLocation(String location, InputStream is)
+    private BundleRevision createRevisionFromLocation(
+        String location, InputStream is, Long revNum)
         throws Exception
     {
         // The revision directory is named using the refresh count and
-        // the revision count. The revision count is obvious, but the
-        // refresh count is less obvious. This is necessary due to how
-        // native libraries are handled in Java; needless to say, every
-        // time a bundle is refreshed we must change the name of its
-        // native libraries so that we can reload them. Thus, we use the
-        // refresh counter as a way to change the name of the revision
-        // directory to give native libraries new absolute names.
+        // the revision number. The revision number is an increasing
+        // counter of the number of times the bundle was revised.
+        // The refresh count is necessary due to how native libraries
+        // are handled in Java; needless to say, every time a bundle is
+        // refreshed we must change the name of its native libraries so
+        // that we can reload them. Thus, we use the refresh counter as
+        // a way to change the name of the revision directory to give
+        // native libraries new absolute names.
         File revisionRootDir = new File(m_archiveRootDir,
-            REVISION_DIRECTORY + getRefreshCount() + "." + getRevisionCount());
+            REVISION_DIRECTORY + getRefreshCount() + "." + revNum.toString());
 
         BundleRevision result = null;