Return-Path: Delivered-To: apmail-incubator-sling-commits-archive@locus.apache.org Received: (qmail 90933 invoked from network); 4 Sep 2008 06:34:34 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 4 Sep 2008 06:34:34 -0000 Received: (qmail 82608 invoked by uid 500); 4 Sep 2008 06:34:32 -0000 Delivered-To: apmail-incubator-sling-commits-archive@incubator.apache.org Received: (qmail 82584 invoked by uid 500); 4 Sep 2008 06:34:32 -0000 Mailing-List: contact sling-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sling-dev@incubator.apache.org Delivered-To: mailing list sling-commits@incubator.apache.org Received: (qmail 82575 invoked by uid 99); 4 Sep 2008 06:34:32 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Sep 2008 23:34:32 -0700 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 04 Sep 2008 06:33:42 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id E8E512388961; Wed, 3 Sep 2008 23:33:42 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r691883 - in /incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl: BundleResourceCache.java BundleResourceProvider.java Date: Thu, 04 Sep 2008 06:33:42 -0000 To: sling-commits@incubator.apache.org From: fmeschbe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20080904063342.E8E512388961@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: fmeschbe Date: Wed Sep 3 23:33:42 2008 New Revision: 691883 URL: http://svn.apache.org/viewvc?rev=691883&view=rev Log: 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 Modified: incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java Modified: incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java?rev=691883&r1=691882&r2=691883&view=diff ============================================================================== --- incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java (original) +++ incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceCache.java Wed Sep 3 23:33:42 2008 @@ -26,59 +26,146 @@ 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 BundleResourceCache 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. + *

+ * 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). + *

+ * 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 NOT_FOUND_CHILDREN = Collections. emptyList(); - private final BundleResourceMap cache; - - private final BundleResourceMap> listCache; - + /** + * Single entry cache. This is a synchronized map with a size limit. + */ + private final Map cache; + + /** + * The child entry cache. This is a synchronized map with a size limit. + */ + private final Map> 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 bundle. + * + * @param bundle + */ BundleResourceCache(Bundle bundle) { this.bundle = bundle; - this.cache = new BundleResourceMap(50); - this.listCache = new BundleResourceMap>(20); + + // create the limited maps wrapping in synchronized maps + this.cache = Collections.synchronizedMap(new BundleResourceMap( + CACHE_SIZE)); + this.listCache = Collections.synchronizedMap(new BundleResourceMap>( + LIST_CACHE_SIZE)); } + /** + * Returns the Bundle 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. + *

+ * This method is backed by the Bundle.getEntry(String) + * method. + * + * @param path The path to the bundle entry to return + * @return The URL to access the bundle entry or null 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 + * parentPath. This parent path is assumed to be an absolute + * path. If relative it is resolved relative to the bundle root. + *

+ * This method is backed by the Bundle.getEntryPaths(String) + * method but returns an Iterator instead of an + * Enumeration of strings. + * + * @param parentPath The path to the parent entry whose child entries are to + * be returned. + * @return An Iterator providing the paths of + * entries considered direct children of the parentPath + * or null if the parent entry does not exist. + */ Iterator getEntryPaths(String path) { List list = listCache.get(path); if (list == null) { - + @SuppressWarnings("unchecked") Enumeration entries = bundle.getEntryPaths(path); if (entries != null && entries.hasMoreElements()) { @@ -87,34 +174,101 @@ 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 extends LinkedHashMap { + + // ---------- 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 BundleResourceMap class extends the + * LinkedHashMap class overwriting the + * {@link #removeEldestEntry(Entry)} method to implement the size limit, + * which is set in the constructor. + */ + private static class BundleResourceMap extends + LinkedHashMap { /** - * 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 true if the current number of elements in the * map exceeds the configured limit. Modified: incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java?rev=691883&r1=691882&r2=691883&view=diff ============================================================================== --- incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java (original) +++ incubator/sling/trunk/extensions/bundleresource/src/main/java/org/apache/sling/bundleresource/impl/BundleResourceProvider.java Wed Sep 3 23:33:42 2008 @@ -48,7 +48,7 @@ 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 @@ void registerService(BundleContext context) { Dictionary props = new Hashtable(); - 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 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 @@ // 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.emptyList().iterator(); + return Collections. emptyList().iterator(); } }