You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:19:02 UTC

[sling-org-apache-sling-bundleresource-impl] 11/25: SLING-641 Make the internal map of the cache be access-ordered (instead of insert-ordered) and wrap the maps in synchronized maps to prevent multi-threading issues plus JavaDoc and preparation for management support

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.bundleresource.impl-2.0.4-incubator
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-bundleresource-impl.git

commit 854122cbdcc583436df1e9271a0b565e8d16623b
Author: Felix Meschberger <fm...@apache.org>
AuthorDate: Thu Sep 4 06:33:42 2008 +0000

    SLING-641 Make the internal map of the cache be access-ordered (instead of
    insert-ordered) and wrap the maps in synchronized maps to prevent
    multi-threading issues plus JavaDoc and preparation for management support
    
    git-svn-id: https://svn.apache.org/repos/asf/incubator/sling/trunk/extensions/bundleresource@691883 13f79535-47bb-0310-9956-ffa450edef68
---
 .../bundleresource/impl/BundleResourceCache.java   | 196 ++++++++++++++++++---
 .../impl/BundleResourceProvider.java               |  38 ++--
 2 files changed, 201 insertions(+), 33 deletions(-)

diff --git a/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java b/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java
index 32d74fb..55b5aa5 100644
--- a/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java
+++ b/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java
@@ -26,59 +26,146 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 
 import org.osgi.framework.Bundle;
 
-public class BundleResourceCache {
+/**
+ * The <code>BundleResourceCache</code> implements a simple caching for
+ * resources provided from a bundle. Each {@link BundleResourceProvider}
+ * instance uses an instance of this class to access the bundle resources (or
+ * bundle entries) through the cache.
+ * <p>
+ * The cache on the one hand caches single entries as URLs. The other part of
+ * the cache is for the child entries of a given bundle entry path. This caches
+ * lists of strings (entry path).
+ * <p>
+ * Currently the cache limits are fixed at {@value #CACHE_SIZE} for the entries
+ * cache and at {@value #LIST_CACHE_SIZE} for the child entries cache.
+ */
+class BundleResourceCache {
+
+    /**
+     * The maximum size of the single entry cache (value is 50).
+     */
+    private static final int CACHE_SIZE = 50;
+
+    /**
+     * The maximum size of the child entry cache (value is 20).
+     */
+    private static final int LIST_CACHE_SIZE = 20;
 
+    /**
+     * Sentinel for the single entry cache representing a missing entry to
+     * prevent looking for non-existing bundle entries multiple times (value is
+     * "file:///not_found").
+     */
     private static final URL NOT_FOUND_URL;
 
+    /**
+     * Sentinel for the child entry cache representing a missing child list for
+     * a given path to prevent looking for non-existing bundle entries multiple
+     * times (value is an empty list).
+     */
     private static final List<String> NOT_FOUND_CHILDREN = Collections.<String> emptyList();
 
-    private final BundleResourceMap<String, URL> cache;
+    /**
+     * Single entry cache. This is a synchronized map with a size limit.
+     */
+    private final Map<String, URL> cache;
 
-    private final BundleResourceMap<String, List<String>> listCache;
+    /**
+     * The child entry cache. This is a synchronized map with a size limit.
+     */
+    private final Map<String, List<String>> listCache;
 
+    /**
+     * The Bundle providing the resource entries.
+     */
     private final Bundle bundle;
 
+    // static initializer setting the NOT_FOUND_URL. Because the
+    // constructor may throw an exception we use a static initializer
+    // which fails the class initialization in the unlikely case
+    // of the URL constructor failing.
     static {
         try {
-            NOT_FOUND_URL = new URL("file:///not_found");
+            NOT_FOUND_URL = new URL("file:/not_found");
         } catch (MalformedURLException mue) {
             throw new ExceptionInInitializerError(mue);
         }
     }
 
+    /**
+     * Creates a new instance of this class providing access to the entries in
+     * the given <code>bundle</code>.
+     * 
+     * @param bundle
+     */
     BundleResourceCache(Bundle bundle) {
         this.bundle = bundle;
-        this.cache = new BundleResourceMap<String, URL>(50);
-        this.listCache = new BundleResourceMap<String, List<String>>(20);
+
+        // create the limited maps wrapping in synchronized maps
+        this.cache = Collections.synchronizedMap(new BundleResourceMap<String, URL>(
+            CACHE_SIZE));
+        this.listCache = Collections.synchronizedMap(new BundleResourceMap<String, List<String>>(
+            LIST_CACHE_SIZE));
     }
 
+    /**
+     * Returns the <code>Bundle</code> to which this instance provides access.
+     */
     Bundle getBundle() {
         return bundle;
     }
-    
+
+    /**
+     * Returns the entry in the underlying bundle at the given path. This path
+     * is assumed to be an absolute path. If relative it is resolved relative to
+     * the bundle root.
+     * <p>
+     * This method is backed by the <code>Bundle.getEntry(String)</code>
+     * method.
+     * 
+     * @param path The path to the bundle entry to return
+     * @return The URL to access the bundle entry or <code>null</code> if the
+     *         bundle does not contain the request entry.
+     */
     URL getEntry(String path) {
         URL url = cache.get(path);
         if (url == null) {
             url = bundle.getEntry(path);
-            
+
             if (url == null) {
                 url = NOT_FOUND_URL;
             }
-            
+
             cache.put(path, url);
         }
 
         return (url == NOT_FOUND_URL) ? null : url;
     }
 
+    /**
+     * Returns a list of bundle entry paths considered children of the given
+     * <code>parentPath</code>. This parent path is assumed to be an absolute
+     * path. If relative it is resolved relative to the bundle root.
+     * <p>
+     * This method is backed by the <code>Bundle.getEntryPaths(String)</code>
+     * method but returns an <code>Iterator<String></code> instead of an
+     * <code>Enumeration</code> of strings.
+     * 
+     * @param parentPath The path to the parent entry whose child entries are to
+     *            be returned.
+     * @return An <code>Iterator<String></code> providing the paths of
+     *         entries considered direct children of the <code>parentPath</code>
+     *         or <code>null</code> if the parent entry does not exist.
+     */
     Iterator<String> getEntryPaths(String path) {
         List<String> list = listCache.get(path);
         if (list == null) {
-            
+
             @SuppressWarnings("unchecked")
             Enumeration<String> entries = bundle.getEntryPaths(path);
             if (entries != null && entries.hasMoreElements()) {
@@ -87,34 +174,101 @@ public class BundleResourceCache {
                     list.add(entries.nextElement());
                 }
             }
-            
+
             if (list == null) {
                 list = NOT_FOUND_CHILDREN;
             }
-            
+
             listCache.put(path, list);
         }
-        
+
         return (list == NOT_FOUND_CHILDREN) ? null : list.iterator();
     }
-    
-    private static class BundleResourceMap<K, V> extends LinkedHashMap<String, V> {
+
+    // ---------- Management API
+
+    /**
+     * Returns the current number of entries stored in the entry cache. This
+     * number includes "negative" entries, which are requested entries not found
+     * in the bundle.
+     */
+    int getEntryCacheSize() {
+        return cache.size();
+    }
+
+    /**
+     * Returns the maximum number of entries to be stored in the cache. This
+     * number is currently fixed at {@link #CACHE_SIZE}
+     */
+    int getEntryCacheMaxSize() {
+        return CACHE_SIZE;
+    }
+
+    /**
+     * Returns the current number of list entries stored in the list cache. This
+     * number includes "negative" list entries, which are requested list entries
+     * not found in the bundle.
+     */
+    int getListCacheSize() {
+        return listCache.size();
+    }
+
+    /**
+     * Returns the maximum number of list entries to be stored in the cache.
+     * This number is currently fixed at {@link #LIST_CACHE_SIZE}
+     */
+    int getListCacheMaxSize() {
+        return LIST_CACHE_SIZE;
+    }
+
+    // ---------- inner class
+
+    /**
+     * The <code>BundleResourceMap</code> class extends the
+     * <code>LinkedHashMap</code> class overwriting the
+     * {@link #removeEldestEntry(Entry)} method to implement the size limit,
+     * which is set in the constructor.
+     */
+    private static class BundleResourceMap<K, V> extends
+            LinkedHashMap<String, V> {
 
         /**
-         * The default size of a bundle resource cache.
+         * The default size of a bundle resource cache (value is 20).
+         */
+        private static final int DEFAULT_LIMIT = 20;
+
+        /**
+         * The limit configured for this map.
          */
-        public static final int DEFAULT_LIMIT = 100;
-        
         private final int limit;
-        
+
+        /**
+         * Creates a new instance of this size limited map.
+         * 
+         * @param limit The maximum number of entries in this map. If this value
+         *            is less than or equal to zero, the default size of
+         *            {@link #DEFAULT_LIMIT} is used.
+         */
         BundleResourceMap(int limit) {
+            // deliberately chosen initial size and load factor, but
+            // we need the access-order to implement the LRU mechanism
+            super(8, 0.75f, true);
+
+            // normalize size to a possitive number
             if (limit <= 0) {
                 limit = DEFAULT_LIMIT;
             }
-            
+
             this.limit = limit;
         }
-        
+
+        /**
+         * Returns the maximum number of entries to be stored in this map.
+         */
+        int getLimit() {
+            return limit;
+        }
+
         /**
          * Returns <code>true</code> if the current number of elements in the
          * map exceeds the configured limit.
diff --git a/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java b/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java
index c9e71ff..0c2230c 100644
--- a/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java
+++ b/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java
@@ -48,7 +48,7 @@ public class BundleResourceProvider implements ResourceProvider {
     private final MappedPath[] roots;
 
     private ServiceRegistration serviceRegistration;
-    
+
     /**
      * Creates Bundle resource provider accessing entries in the given Bundle an
      * supporting resources below root paths given by the rootList which is a
@@ -70,38 +70,51 @@ public class BundleResourceProvider implements ResourceProvider {
 
     void registerService(BundleContext context) {
         Dictionary<String, Object> props = new Hashtable<String, Object>();
-        props.put(Constants.SERVICE_DESCRIPTION, "Provider of Bundle based Resources");
+        props.put(Constants.SERVICE_DESCRIPTION,
+            "Provider of Bundle based Resources");
         props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
         props.put(ROOTS, getRoots());
-        
+
         serviceRegistration = context.registerService(SERVICE_NAME, this, props);
     }
-    
+
     void unregisterService() {
         if (serviceRegistration != null) {
             serviceRegistration.unregister();
         }
     }
+
+    //---------- Web Console plugin support
+    
+    BundleResourceCache getBundleResourceCache() {
+        return bundle;
+    }
+
+    MappedPath[] getMappedPaths() {
+        return roots;
+    }
+    
+    //---------- internal
     
     /** Returns the root paths */
     private String[] getRoots() {
         String[] rootPaths = new String[roots.length];
-        for (int i=0; i < roots.length; i++) {
+        for (int i = 0; i < roots.length; i++) {
             rootPaths[i] = roots[i].getResourceRoot();
         }
         return rootPaths;
     }
-    
+
     private MappedPath getMappedPath(String resourcePath) {
-        for (MappedPath mappedPath: roots) {
+        for (MappedPath mappedPath : roots) {
             if (mappedPath.isChild(resourcePath)) {
                 return mappedPath;
             }
         }
-        
+
         return null;
     }
-    
+
     public Resource getResource(ResourceResolver resourceResolver,
             HttpServletRequest request, String path) {
         return getResource(resourceResolver, path);
@@ -115,9 +128,10 @@ public class BundleResourceProvider implements ResourceProvider {
     public Resource getResource(ResourceResolver resourceResolver, String path) {
         MappedPath mappedPath = getMappedPath(path);
         if (mappedPath != null) {
-            return BundleResource.getResource(resourceResolver, bundle, mappedPath, path);
+            return BundleResource.getResource(resourceResolver, bundle,
+                mappedPath, path);
         }
-        
+
         return null;
     }
 
@@ -140,6 +154,6 @@ public class BundleResourceProvider implements ResourceProvider {
         // the parent resource cannot have children in this provider,
         // though this is basically not expected, we still have to
         // be prepared for such a situation
-        return Collections.<Resource>emptyList().iterator();
+        return Collections.<Resource> emptyList().iterator();
     }
 }

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.