You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ie...@apache.org on 2009/10/16 00:05:40 UTC

svn commit: r825678 - in /sling/trunk/contrib/jcr/resource2: ./ src/main/java/org/apache/sling/jcr/resource/internal/ src/main/java/org/apache/sling/jcr/resource/internal/helper/ src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/ src/test...

Author: ieb
Date: Thu Oct 15 22:05:39 2009
New Revision: 825678

URL: http://svn.apache.org/viewvc?rev=825678&view=rev
Log:
SLING-1156
Patched the ResourceProviderEntry into ResourceProviderEntry2 to create a Tree of Maps for resolution.

Added:
    sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java   (with props)
Modified:
    sling/trunk/contrib/jcr/resource2/pom.xml
    sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java
    sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
    sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java
    sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java
    sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java

Modified: sling/trunk/contrib/jcr/resource2/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/pom.xml?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/pom.xml (original)
+++ sling/trunk/contrib/jcr/resource2/pom.xml Thu Oct 15 22:05:39 2009
@@ -26,11 +26,11 @@
         <relativePath>../../../parent/pom.xml</relativePath>
     </parent>
 
-    <artifactId>org.apache.sling.jcr.resource</artifactId>
+    <artifactId>org.apache.sling.jcr.resource2</artifactId>
     <version>2.0.7-SNAPSHOT</version>
     <packaging>bundle</packaging>
 
-    <name>Apache Sling Resource Resolver</name>
+    <name>Apache Sling Resource Resolver (Tree of Maps)</name>
     <description>
         This bundle provides the JCR based ResourceResolver.
     </description>

Modified: sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java (original)
+++ sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java Thu Oct 15 22:05:39 2009
@@ -92,6 +92,7 @@
 
     public JcrResourceResolver2(JcrResourceProviderEntry rootProvider,
             JcrResourceResolverFactoryImpl factory, MapEntries resourceMapper) {
+        
         this.rootProvider = rootProvider;
         this.factory = factory;
         this.resourceMapper = resourceMapper;

Modified: sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java (original)
+++ sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java Thu Oct 15 22:05:39 2009
@@ -32,6 +32,7 @@
 
 import org.apache.commons.collections.BidiMap;
 import org.apache.commons.collections.bidimap.TreeBidiMap;
+import org.apache.jackrabbit.name.Path.RootElement;
 import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
@@ -42,8 +43,7 @@
 import org.apache.sling.jcr.resource.JcrResourceTypeProvider;
 import org.apache.sling.jcr.resource.internal.helper.MapEntries;
 import org.apache.sling.jcr.resource.internal.helper.Mapping;
-import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
-import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntryException;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2;
 import org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderEntry;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
@@ -194,7 +194,7 @@
     // the search path for ResourceResolver.getResource(String)
     private String[] searchPath;
 
-    private ResourceProviderEntry rootProviderEntry;
+    private ResourceProviderEntry2 rootProviderEntry;
 
     // whether to mangle paths with namespaces or not
     private boolean mangleNamespacePrefixes;
@@ -211,7 +211,8 @@
     private DynamicClassLoaderManager dynamicClassLoaderManager;
 
     public JcrResourceResolverFactoryImpl() {
-        this.rootProviderEntry = new ResourceProviderEntry("/", null, null);
+        this.rootProviderEntry = new ResourceProviderEntry2("/", null);
+
     }
 
     // ---------- JcrResourceResolverFactory -----------------------------------
@@ -224,6 +225,7 @@
         JcrResourceProviderEntry sessionRoot = new JcrResourceProviderEntry(
             session, rootProviderEntry, getJcrResourceTypeProviders(),
             this.getDynamicClassLoader());
+        
 
         return new JcrResourceResolver2(sessionRoot, this, mapEntries);
     }
@@ -288,7 +290,7 @@
      *
      * @return Our rootProviderEntry
      */
-    protected ResourceProviderEntry getRootProviderEntry() {
+    protected ResourceProviderEntry2 getRootProviderEntry() {
         return rootProviderEntry;
     }
 
@@ -480,23 +482,16 @@
                             root = root.substring(0, root.length() - 1);
                         }
 
-                        try {
-                            rootProviderEntry.addResourceProvider(root,
-                                provider);
-
-                            log.debug("bindResourceProvider: {}={} ({})",
-                                new Object[] { root, provider, serviceName });
-                            if ( localEA != null ) {
-                                final Dictionary<String, Object> props = new Hashtable<String, Object>();
-                                props.put(SlingConstants.PROPERTY_PATH, root);
-                                localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED,
-                                        props));
-                            }
-                        } catch (ResourceProviderEntryException rpee) {
-                            log.error(
-                                "bindResourceProvider: Cannot register ResourceProvider {} for {}: ResourceProvider {} is already registered",
-                                new Object[] { provider, root,
-                                    rpee.getExisting().getResourceProvider() });
+                        rootProviderEntry.addResourceProvider(root,
+                            provider);
+
+                        log.debug("bindResourceProvider: {}={} ({})",
+                            new Object[] { root, provider, serviceName });
+                        if ( localEA != null ) {
+                            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+                            props.put(SlingConstants.PROPERTY_PATH, root);
+                            localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED,
+                                    props));
                         }
                     }
                 }
@@ -520,6 +515,10 @@
             // synchronized insertion of new resource providers into
             // the tree to not inadvertently loose an entry
             synchronized (this) {
+               if ( componentContext != null ) {
+                ResourceProvider provider = (ResourceProvider) componentContext.locateService(
+                   "ResourceProvider", reference);
+                   
 
                 for (String root : roots) {
                     // cut off trailing slash
@@ -530,7 +529,7 @@
                     // TODO: Do not remove this path, if another resource
                     // owns it. This may be the case if adding the provider
                     // yielded an ResourceProviderEntryException
-                    rootProviderEntry.removeResourceProvider(root);
+                    rootProviderEntry.removeResourceProvider(root, provider);
 
                     log.debug("unbindResourceProvider: root={} ({})", root,
                         serviceName);
@@ -541,6 +540,7 @@
                                 props));
                     }
                 }
+               }
             }
         }
 
@@ -626,4 +626,12 @@
 
         return null;
     }
+    
+    
+    public void run() {
+        String stat = rootProviderEntry.getResolutionStats();
+        if ( stat != null ) {
+            log.info(stat);   
+        }
+    }
 }

Added: sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java?rev=825678&view=auto
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java (added)
+++ sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java Thu Oct 15 22:05:39 2009
@@ -0,0 +1,641 @@
+/*
+ * 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.sling.jcr.resource.internal.helper;
+
+import org.apache.commons.collections.FastTreeMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * The <code>ResourceProviderEntry</code> class represents a node in the tree of
+ * resource providers spanned by the root paths of the provider resources.
+ * <p>
+ * This class is comparable to itself to help keep the child entries list sorted
+ * by their prefix.
+ */
+public class ResourceProviderEntry2 implements
+        Comparable<ResourceProviderEntry2> {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 7420631325909144862L;
+    
+    private static Logger LOGGER = LoggerFactory.getLogger(ResourceProviderEntry2.class);
+
+    // the path to resources provided by the resource provider of this
+    // entry. this path is relative to the path of the parent resource
+    // provider entry and has no trailing slash.
+    private final String path;
+
+    // the path to resources provided by the resource provider of this
+    // entry. this is the same path as the path field but with a trailing
+    // slash to be used as a prefix match resource paths to resolve
+    private final String prefix;
+
+    // the resource provider kept in this entry supporting resources at and
+    // below the path of this entry.
+    private ResourceProvider[] providers = new ResourceProvider[0];
+
+    private long ttime = 0L;
+
+    private long nmiss = 0L;
+
+    private long nsynthetic = 0L;
+
+    private long nreal = 0L;
+
+    private FastTreeMap storageMap = new FastTreeMap();
+
+    /**
+     * Creates an instance of this class with the given path relative to the
+     * parent resource provider entry, encapsulating the given ResourceProvider,
+     * and a number of inital child entries.
+     * 
+     * @param path
+     *            The relative path supported by the provider
+     * @param provider
+     *            The resource provider to encapsulate by this entry.
+     */
+    public ResourceProviderEntry2(String path, ResourceProvider[] provider) {
+        if (path.endsWith("/")) {
+            this.path = path.substring(0, path.length() - 1);
+            this.prefix = path;
+        } else {
+            this.path = path;
+            this.prefix = path + "/";
+        }
+        this.providers = provider;
+        
+        // this will consume slightly more memory but ensures read is fast.
+        storageMap.setFast(true);
+    }
+
+    /**
+     * Returns the resource provider contained in this entry
+     */
+    public ResourceProvider[] getResourceProviders() {
+        return providers;
+    }
+
+    /**
+     * Returns the resource with the given path or <code>null</code> if neither
+     * the resource provider of this entry nor the resource provider of any of
+     * the child entries can provide the resource.
+     * 
+     * @param path
+     *            The path to the resource to return.
+     * @return The resource for the path or <code>null</code> if no resource can
+     *         be found.
+     * @throws org.apache.sling.api.SlingException
+     *             if an error occurrs trying to access an existing resource.
+     */
+    public Resource getResource(ResourceResolver resourceResolver, String path) {
+        return getInternalResource(resourceResolver, path);
+    }
+
+    public Iterator<Resource> listChildren(final Resource resource) {
+        LOGGER.debug("Child Iterator for {}",resource.getPath());
+        return new Iterator<Resource>() {
+            private final Iterator<ResourceProvider> providers;
+
+            private Iterator<Resource> resources;
+
+            private Resource nextResource;
+
+            private Map<String, Resource> delayed;
+
+            private Set<String> visited;
+
+            private Iterator<Resource> delayedIter;
+
+            {
+                String path = resource.getPath();
+                if (!path.endsWith("/")) {
+                    path += "/";
+                }
+
+                // gather the providers in linked set, such that we keep
+                // the order of addition and make sure we only get one entry
+                // for each resource provider
+                Set<ResourceProvider> providersSet = new LinkedHashSet<ResourceProvider>();
+                getResourceProviders(path, providersSet);
+                
+                LOGGER.debug(" Provider Set for path {} {} ",path,Arrays.toString(providersSet.toArray(new ResourceProvider[0])));
+
+                providers = providersSet.iterator();
+                delayed = new HashMap<String, Resource>();
+                visited = new HashSet<String>();
+                nextResource = seek();
+            }
+
+            public boolean hasNext() {
+                return nextResource != null;
+            }
+
+            public Resource next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+
+                Resource result = nextResource;
+                nextResource = seek();
+                LOGGER.debug("  Child Resoruce {} ", result.getPath());
+                return result;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException("remove");
+            }
+
+            private Resource seek() {
+                for (;;) {
+                    while ((resources == null || !resources.hasNext())
+                            && providers.hasNext()) {
+                        ResourceProvider provider = providers.next();
+                        resources = provider.listChildren(resource);
+                        LOGGER.debug("     Checking Provider {} ", provider);
+                    }
+
+                    if (resources != null && resources.hasNext()) {
+                        Resource res = resources.next();
+                        String resPath = res.getPath();
+                        LOGGER.debug("      resource {} ", res.getPath());
+
+                        if (visited.contains(resPath)) {
+
+                            // ignore a path, we have already visited and
+                            // ensure it will not be listed as a delayed
+                            // resource at the end
+                            delayed.remove(resPath);
+
+                        } else if (res instanceof SyntheticResource) {
+
+                            // don't return synthetic resources right away,
+                            // since a concrete resource for the same path
+                            // may be provided later on
+                            delayed.put(resPath, res);
+
+                        } else {
+
+                            // we use this concrete, unvisited resource but
+                            // mark it as visited
+                            visited.add(resPath);
+                            return res;
+
+                        }
+                    } else {
+                        break;
+                    }
+                }
+
+                // we exhausted all resource providers with their concrete
+                // resources. now lets do the delayed (synthetic) resources
+                if (delayedIter == null) {
+                    delayedIter = delayed.values().iterator();
+                }
+                Resource res = delayedIter.hasNext() ? delayedIter.next() : null;
+                if ( res != null ) {
+                    LOGGER.debug("   D  resource {} ", res.getPath());
+                }
+                return res;
+            }
+        };
+    }
+
+    /**
+     * Adds the given resource provider into the tree for the given prefix.
+     * 
+     * @return <code>true</code> if the provider could be entered into the
+     *         subtree below this entry. Otherwise <code>false</code> is
+     *         returned.
+     */
+    public boolean addResourceProvider(String prefix, ResourceProvider provider) {
+        synchronized (this) {
+            String[] elements = split(prefix, '/');
+            List<ResourceProviderEntry2> entryPath = getResourceProviderPath(elements);
+            entryPath.add(this); // add this to the end
+            // I want to add to the end, but remember the list of entries starts
+            // with the root.
+            Collections.reverse(entryPath);
+            for (int i = entryPath.size() - 1; i < elements.length; i++) {
+                String stubPrefix = elements[i];
+                ResourceProviderEntry2 rpe2 = new ResourceProviderEntry2(
+                        stubPrefix, new ResourceProvider[0]);
+                entryPath.get(i).put(elements[i], rpe2);
+                entryPath.add(rpe2);
+            }
+            return entryPath.get(elements.length).addInternalProvider(provider);
+
+        }
+    }
+
+
+    //------------------ Map methods, here so that we can delegate 2 maps together
+    /**
+     * @param string
+     * @param rpe2
+     */
+    public void put(String key, ResourceProviderEntry2 value) {
+        storageMap.put(key,value);
+    }
+    
+    /**
+     * @param element
+     * @return
+     */
+    public boolean containsKey(String key) {
+        return storageMap.containsKey(key);
+    }
+
+
+    /**
+     * @param element
+     * @return
+     */
+    public ResourceProviderEntry2 get(String key) {
+        return (ResourceProviderEntry2) storageMap.get(key);
+    }
+
+    /**
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    public Collection<ResourceProviderEntry2> values() {
+        return storageMap.values();
+    }
+
+    public boolean removeResourceProvider(String prefix,
+            ResourceProvider provider) {
+        synchronized (this) {
+            String[] elements = split(prefix, '/');
+            List<ResourceProviderEntry2> entryPath = getResourceProviderPath(elements);
+            if (entryPath.size() == elements.length) {
+                // the first element is a perfect match;
+                return entryPath.get(0).removeInternalProvider(provider);
+            }
+            return false;
+        }
+    }
+
+    // ---------- Comparable<ResourceProviderEntry> interface ------------------
+
+    public int compareTo(ResourceProviderEntry2 o) {
+        return prefix.compareTo(o.prefix);
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    /**
+     * Adds a list of providers to this entry.
+     * 
+     * @param provider
+     */
+    private boolean addInternalProvider(ResourceProvider provider) {
+        synchronized (providers) {
+            int before = providers.length;
+            Set<ResourceProvider> set = new HashSet<ResourceProvider>();
+            if (providers != null) {
+                set.addAll(Arrays.asList(providers));
+            }
+            set.add(provider);
+            providers = conditionalSort(set);
+            return providers.length > before;
+        }
+
+    }
+
+    /**
+     * @param provider
+     * @return
+     */
+    private boolean removeInternalProvider(ResourceProvider provider) {
+        synchronized (providers) {
+            int before = providers.length;
+            Set<ResourceProvider> set = new HashSet<ResourceProvider>();
+            if (providers != null) {
+                set.addAll(Arrays.asList(providers));
+            }
+            set.remove(provider);
+            providers = conditionalSort(set);
+            return providers.length < before;
+        }
+    }
+
+    /**
+     * @param set
+     * @return
+     */
+    private ResourceProvider[] conditionalSort(Set<ResourceProvider> set) {
+        //
+        // convert to a list so we can selectively sort. We can't guarantee that
+        // all
+        // ResourceProviders implement Comparable so can't use a TreeSet.
+        // However, using a set in the first instance ensures that no one relies
+        // on the add
+        // order.
+
+        List<ResourceProvider> providerList = new ArrayList<ResourceProvider>(
+                set);
+        // ResourceProviders that need to get into the list ahead of others must
+        // implement
+        // the comparable interface. Ones that don't implement the comparable
+        // interface get
+        // placed in a group in the center ordered by the way they were added.
+        // eg high-priority ..... non conflicting .... explicitly low priority
+        // In general if a ResourceProvider will only ever respond with non null
+        // to paths
+        // that no other resource provider will respond with non null its non
+        // conflicting and
+        // so does not require special treatment (eg no Comparable required)
+
+        Collections.sort(providerList, new Comparator<ResourceProvider>() {
+
+            @SuppressWarnings("unchecked")
+            public int compare(ResourceProvider o1, ResourceProvider o2) {
+                if (o1 instanceof Comparable) {
+                    Comparable c1 = (Comparable) o1;
+                    return c1.compareTo(o2);
+                } else if (o2 instanceof Comparable) {
+                    Comparable c2 = (Comparable) o2;
+                    return c2.compareTo(o1) * (-1);
+                }
+                return 0;
+            }
+        });
+
+        return set.toArray(new ResourceProvider[set.size()]);
+    }
+
+    /**
+     * Get a of ResourceProvidersEntries leading to the fullPath in reverse
+     * order.
+     * 
+     * @param fullPath
+     *            the full path
+     * @return a reverse order list of ProviderEntries to the path.
+     */
+    private List<ResourceProviderEntry2> getResourceProviderPath(
+            String[] elements) {
+        ResourceProviderEntry2 base = this;
+        List<ResourceProviderEntry2> providerEntryPath = new ArrayList<ResourceProviderEntry2>();
+        if (elements != null) {
+            for (String element : elements) {
+                if (element != null) {
+                    if (base.containsKey(element)) {
+                        base = base.get(element);
+                        providerEntryPath.add(base);
+                    } else {
+                        break;
+                    }
+                }
+            }
+            Collections.reverse(providerEntryPath);
+        }
+        return providerEntryPath;
+    }
+
+
+    /**
+     * Resolve a resource from a path into a Resource
+     * 
+     * @param resolver
+     *            the ResourceResolver.
+     * @param fullPath
+     *            the Full path
+     * @return null if no resource was found, a resource if one was found.
+     */
+    private Resource getInternalResource(ResourceResolver resourceResolver,
+            String fullPath) {
+        long start = System.currentTimeMillis();
+        try {
+
+            if (fullPath == null || fullPath.length() == 0
+                    || fullPath.charAt(0) != '/') {
+                nmiss++;
+                LOGGER.debug("Not absolute {} :{}",fullPath,(System.currentTimeMillis() - start));
+                return null; // fullpath must be absolute
+            }
+            String[] elements = split(fullPath, '/'); // should use something
+            // more efficient here.
+            List<ResourceProviderEntry2> list = getResourceProviderPath(elements);
+            // the path is in reverse order end first
+
+            int i = 0;
+            for (ResourceProviderEntry2 rpe : list) {
+                ResourceProvider[] rps = rpe.getResourceProviders();
+                int j = 0;
+                for (ResourceProvider rp : rps) {
+
+                    Resource resource = rp.getResource(resourceResolver,
+                            fullPath);
+                    if (resource != null) {
+                        nreal++;
+                        LOGGER.debug("Resolved Full {} using {} from {} ",new Object[]{
+                                fullPath, rp, Arrays.toString(rps)});
+                        return resource;
+                    }
+                    j++;
+                }
+                i++;
+            }
+            // query: /libs/sling/servlet/default
+            // resource Provider: libs/sling/servlet/default/GET.servlet
+            // list will match libs, sling, servlet, default 
+            // and there will be no resource provider at the end (element 0)
+            if (list.size() > 0 && list.size() == elements.length ) {
+                if ( list.get(0).getResourceProviders().length == 0 ) {
+                    nsynthetic++;
+                    LOGGER.debug("Resolved Synthetic {}", fullPath);
+                    return new SyntheticResource(resourceResolver,
+                            fullPath,
+                            ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+                }
+            }
+
+            // resolve against this one
+            ResourceProvider[] rps = getResourceProviders();
+            int j = 0;
+            for (ResourceProvider rp : rps) {
+                Resource resource = rp.getResource(resourceResolver, fullPath);
+                if (resource != null) {
+                    nreal++;
+                    LOGGER.debug("Resolved Base {} using {} ", fullPath, rp);
+                    return resource;
+                }
+                j++;
+            }
+
+
+            LOGGER.debug("Resource null {} ", fullPath);
+            nmiss++;
+            return null;
+        } catch (Exception ex) {
+            LOGGER.debug("Failed! ",ex);
+            return null;
+        } finally {
+            ttime += System.currentTimeMillis() - start;
+        }
+    }
+
+
+    /**
+     * Returns all resource providers which provider resources whose prefix is
+     * the given path.
+     * 
+     * @param path
+     *            The prefix path to match the resource provider roots against
+     * @param providers
+     *            The set of already found resource providers to which any
+     *            additional resource providers are added.
+     */
+    private void getResourceProviders(String path,
+            Set<ResourceProvider> providers) {
+        String[] elements = split(path, '/');
+        ResourceProviderEntry2 base = this;
+        for (String element : elements ) {
+            if ( base.containsKey(element)) {
+                base = base.get(element);
+            } else {
+                base = null;
+                break;
+            }
+        }
+        // the path has been exausted and there is a subtree to be collected, so go and collect it.
+        if ( base != null ) {
+            getResourceProviders(base, providers);
+        }
+        // add in providers at this node in the tree, ie the root provider
+        for ( ResourceProvider rp : getResourceProviders() ) {
+            providers.add(rp);
+        }
+
+    }
+
+    /**
+     * @param base
+     * @param providers2
+     */
+    private void getResourceProviders(ResourceProviderEntry2 entry,
+            Set<ResourceProvider> providers) {
+        // recurse down the tree
+        LOGGER.debug(" Gathering For {} ",entry.prefix);
+        for ( ResourceProviderEntry2 e : entry.values() ) {
+            getResourceProviders(e, providers);
+        }
+        // add in providers at this node in the tree.
+        for ( ResourceProvider rp : entry.getResourceProviders() ) {
+            providers.add(rp);
+        }
+    }
+
+    /**
+     * @param st
+     * @param sep
+     * @return an array of the strings between the separator
+     */
+    private String[] split(String st, char sep) {
+
+        if (st == null) {
+            return new String[0];
+        }
+        char[] pn = st.toCharArray();
+        if (pn.length == 0) {
+            return new String[0];
+        }
+        if (pn.length == 1 && pn[0] == sep) {
+            return new String[0];
+        }
+        int n = 1;
+        int start = 0;
+        int end = pn.length;
+        while (start < end && sep == pn[start])
+            start++;
+        while (start < end && sep == pn[end - 1])
+            end--;
+        for (int i = start; i < end; i++) {
+            if (sep == pn[i]) {
+                n++;
+            }
+        }
+        String[] e = new String[n];
+        int s = start;
+        int j = 0;
+        for (int i = start; i < end; i++) {
+            if (pn[i] == sep) {
+                e[j++] = new String(pn, s, i - s);
+                s = i + 1;
+            }
+        }
+        if (s < end) {
+            e[j++] = new String(pn, s, end - s);
+        }
+        return e;
+    }
+
+    /**
+     * @return
+     */
+    public String getResolutionStats() {
+        long tot = nreal + nsynthetic + nmiss;
+        if (tot == 0) {
+            return null;
+        }
+        float n = tot;
+        float t = ttime;
+        float persec = 1000 * n / t;
+        float avgtime = t / n;
+
+        String stat = "Resolved: Real(" + nreal + ") Synthetic(" + nsynthetic
+                + ") Missing(" + nmiss + ") Total(" + tot + ") at " + persec
+                + " ops/sec avg " + avgtime + " ms";
+        ttime = nmiss = nsynthetic = nreal = 0L;
+        return stat;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.util.AbstractMap#toString()
+     */
+    @Override
+    public String toString() {
+        return this.path;
+        //"{path:\"" + this.path + "\", providers:"+Arrays.toString(getResourceProviders())+", map:" + storageMap.toString() + "}";
+    }
+
+}

Propchange: sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry2.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java (original)
+++ sling/trunk/contrib/jcr/resource2/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java Thu Oct 15 22:05:39 2009
@@ -22,21 +22,31 @@
 
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.jcr.resource.JcrResourceTypeProvider;
-import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2;
 
-public class JcrResourceProviderEntry extends ResourceProviderEntry {
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 
-    private final ResourceProviderEntry delegatee;
+public class JcrResourceProviderEntry extends ResourceProviderEntry2 {
+
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 5672648586247261128L;
+
+    private final ResourceProviderEntry2 delegatee;
 
     private final Session session;
 
     private final JcrResourceTypeProvider[] resourceTypeProviders;
 
     public JcrResourceProviderEntry(Session session,
-                                    ResourceProviderEntry delegatee,
-                                    JcrResourceTypeProvider[] resourceTypeProviders,
-                                    final ClassLoader dynamicClassLoader) {
-        super("/", new JcrResourceProvider(session, resourceTypeProviders, dynamicClassLoader), null);
+            ResourceProviderEntry2 delegatee,
+            JcrResourceTypeProvider[] resourceTypeProviders,
+            final ClassLoader dynamicClassLoader) {
+        super("/", new ResourceProvider[] { new JcrResourceProvider(session,
+                resourceTypeProviders, dynamicClassLoader) });
 
         this.delegatee = delegatee;
         this.session = session;
@@ -52,17 +62,99 @@
     }
 
     @Override
-    public ResourceProviderEntry[] getEntries() {
-        return delegatee.getEntries();
+    public boolean addResourceProvider(String prefix, ResourceProvider provider) {
+        return delegatee.addResourceProvider(prefix, provider);
     }
 
     @Override
-    public boolean addResourceProvider(String prefix, ResourceProvider provider) {
-        return delegatee.addResourceProvider(prefix, provider);
+    public boolean removeResourceProvider(String prefix,
+            ResourceProvider provider) {
+        return delegatee.removeResourceProvider(prefix, provider);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#put(java.lang.String,
+     *      org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2)
+     */
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#get(java.lang.String)
+     */
+    @Override
+    public ResourceProviderEntry2 get(String key) {
+        ResourceProviderEntry2 rpe = super.get(key);
+        if (rpe == null) {
+            rpe = delegatee.get(key);
+        }
+        return rpe;
     }
 
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#values()
+     */
     @Override
-    public boolean removeResourceProvider(String prefix) {
-        return delegatee.removeResourceProvider(prefix);
+    public Collection<ResourceProviderEntry2> values() {
+        List<ResourceProviderEntry2> list = new ArrayList<ResourceProviderEntry2>(
+                super.values());
+        list.addAll(delegatee.values());
+        return list;
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#containsKey(java.lang.String)
+     */
+    @Override
+    public boolean containsKey(String key) {
+        return (super.containsKey(key) || delegatee.containsKey(key));
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#getResourceProviders()
+     */
+    @Override
+    public ResourceProvider[] getResourceProviders() {
+        // merge the delegatee and this set of providers,
+        // its probably better to do this every time, as
+
+        ResourceProvider[] delegateeProviders = delegatee
+                .getResourceProviders();
+        ResourceProvider[] superProviders = super.getResourceProviders();
+        if (delegateeProviders == null && superProviders == null) {
+            return null;
+        }
+        if (delegateeProviders == null) {
+            delegateeProviders = new ResourceProvider[0];
+        }
+        if (superProviders == null) {
+            superProviders = new ResourceProvider[0];
+        }
+        ResourceProvider[] resourceProviders = new ResourceProvider[delegateeProviders.length
+                + superProviders.length];
+        System.arraycopy(delegateeProviders, 0, resourceProviders, 0,
+                delegateeProviders.length);
+        System.arraycopy(superProviders, 0, resourceProviders,
+                delegateeProviders.length, superProviders.length);
+        return resourceProviders;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#toString()
+     */
+    @Override
+    public String toString() {
+        return delegatee.toString();
+    }
+
 }

Modified: sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java (original)
+++ sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java Thu Oct 15 22:05:39 2009
@@ -144,6 +144,7 @@
         super.tearDown();
     }
 
+    @SuppressWarnings("deprecation")
     public void testBasicAPIAssumptions() throws Exception {
 
         // null resource is accessing /, which exists of course
@@ -195,7 +196,7 @@
         final HttpServletRequest req2 = new ResourceResolverTestRequest(null);
         final Resource res2 = resResolver.resolve(req2);
         assertNotNull("Expecting resource if resolution fails", res2);
-        assertTrue("Resource must not be NonExistingResource",
+        assertTrue("Resource must not be NonExistingResource was ",
             !(res2 instanceof NonExistingResource));
         assertEquals("Path must be the the root path", "/", res2.getPath());
     }

Modified: sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java?rev=825678&r1=825677&r2=825678&view=diff
==============================================================================
--- sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java (original)
+++ sling/trunk/contrib/jcr/resource2/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java Thu Oct 15 22:05:39 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.jcr.resource.internal.helper;
 
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Map;
 
@@ -25,23 +26,25 @@
 
 import junit.framework.TestCase;
 
+import org.apache.sling.api.resource.NonExistingResource;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.SyntheticResource;
 
 public class ResourceProviderEntryTest extends TestCase {
 
     private ResourceProvider rootProvider;
 
-    private ResourceProviderEntry root;
+    private ResourceProviderEntry2 root;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
         rootProvider = new TestResourceProvider("/");
-        root = new ResourceProviderEntry("/", rootProvider, null);
+        root = new ResourceProviderEntry2("/", new ResourceProvider[]{ rootProvider});
     }
 
     @Override
@@ -63,6 +66,7 @@
         String firstPath = "/rootel";
         ResourceProvider first = new TestResourceProvider(firstPath);
         root.addResourceProvider(firstPath, first);
+        
 
         assertEquals(root, root.getResource(null, "/"));
         assertEquals(first, root.getResource(null, "/rootel"));
@@ -88,6 +92,8 @@
         root.addResourceProvider(secondPath, second);
         root.addResourceProvider(thirdPath, third);
 
+        
+
         assertEquals(rootProvider, root.getResource(null, "/"));
         assertEquals(first, root.getResource(null, "/rootel"));
         assertEquals(first, root.getResource(null, "/rootel/html.js"));
@@ -95,8 +101,9 @@
         assertEquals(second, root.getResource(null, "/rootel/child/html.js"));
         assertEquals(third,
             root.getResource(null, "/apps/sling/sample/html.js"));
-        assertEquals(rootProvider, root.getResource(null,
-            "/apps/sling/microsling/html.js"));
+        Resource resource = root.getResource(null,
+            "/apps/sling/microsling/html.js");
+            assertEquals(rootProvider, resource);
     }
 
     public void testAdd3ProvidersReverse() {
@@ -118,9 +125,10 @@
         assertEquals(second, root.getResource(null, "/rootel/child"));
         assertEquals(second, root.getResource(null, "/rootel/child/html.js"));
         assertEquals(third,
-            root.getResource(null, "/apps/sling/sample/html.js"));
-        assertEquals(rootProvider, root.getResource(null,
-            "/apps/sling/microsling/html.js"));
+           root.getResource(null, "/apps/sling/sample/html.js"));
+        Resource resource = root.getResource(null,
+              "/apps/sling/microsling/html.js");
+        assertEquals(rootProvider, resource);
     }
 
     public void testRemoveProviders() {
@@ -140,9 +148,11 @@
         assertEquals(first, root.getResource(null, "/rootel/html.js"));
         assertEquals(second, root.getResource(null, "/rootel/child/html.js"));
 
-        root.removeResourceProvider(firstPath);
+        root.removeResourceProvider(firstPath, first);
+        
 
         assertEquals(rootProvider, root.getResource(null, "/"));
+        assertEquals(rootProvider, root.getResource(null, "/rootel/sddsf/sdfsdf/html.js"));
         assertEquals(rootProvider, root.getResource(null, "/rootel/html.js"));
         assertEquals(second, root.getResource(null, "/rootel/child/html.js"));
 
@@ -150,17 +160,22 @@
 
         assertEquals(rootProvider, root.getResource(null, "/"));
         assertEquals(first, root.getResource(null, "/rootel/html.js"));
-        assertEquals(second, root.getResource(null, "/rootel/child/html.js"));
+        assertEquals(second,root.getResource(null, "/rootel/child/html.js"));
     }
 
     protected void assertEquals(ResourceProvider resProvider, Resource res) {
         assertEquals(resProvider, res.getResourceResolver());
     }
 
-    protected void assertEquals(ResourceProviderEntry resProviderEntry,
+    protected void assertEquals(ResourceProviderEntry2 resProviderEntry,
             Resource res) {
-        assertEquals(resProviderEntry.getResourceProvider(),
-            res.getResourceResolver());
+        ResourceProvider[] resourceProviders = resProviderEntry.getResourceProviders();
+        for ( ResourceProvider rp : resourceProviders ) {
+            if ( rp.equals(res.getResourceResolver())) {
+                return;
+            }
+        }
+        fail();
     }
 
     // The test provider implements the ResourceResolver interface and sets
@@ -237,6 +252,16 @@
         public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
             return null;
         }
+        
+        /**
+         * {@inheritDoc}
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return Arrays.toString(roots);
+        }
+
     }
 
     private static class TestResource implements Resource {