You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2007/12/05 21:38:26 UTC

svn commit: r601511 - in /incubator/sling/trunk/jcr/resource/src: main/java/org/apache/sling/jcr/resource/ main/java/org/apache/sling/jcr/resource/internal/ main/java/org/apache/sling/jcr/resource/internal/helper/ main/java/org/apache/sling/jcr/resourc...

Author: fmeschbe
Date: Wed Dec  5 12:38:23 2007
New Revision: 601511

URL: http://svn.apache.org/viewvc?rev=601511&view=rev
Log:
SLING-122 Add support Bundle based resources and adapt resource resolution to be
pluggable also supporting other resource providers in the future

Added:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProvider.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResource.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceIterator.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceProvider.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLConnection.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLStreamHandler.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
      - copied, changed from r600523, incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResource.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java
      - copied, changed from r600457, incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIterator.java
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java
Removed:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResource.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIterator.java
Modified:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIteratorTest.java

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java?rev=601511&r1=601510&r2=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java Wed Dec  5 12:38:23 2007
@@ -88,5 +88,17 @@
      */
     public static final String MAPPING_NODE_TYPE = "MAPPED_NODE_TYPE";
 
+    /**
+     * The name of the bundle manifest header listing the bundle entries
+     * providing Object Content Mapping configurations (value is
+     * "Sling-Mappings").
+     */
     public static final String MAPPER_BUNDLE_HEADER = "Sling-Mappings";
+
+    /**
+     * The name of the bundle manifest header listing the resource provider root
+     * paths provided by the bundle (value is "Sling-Bundle-Resources").
+     */
+    public static final String BUNDLE_RESOURCE_ROOTS = "Sling-Bundle-Resources";
+
 }

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java?rev=601511&r1=601510&r2=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java Wed Dec  5 12:38:23 2007
@@ -48,10 +48,11 @@
 import org.apache.sling.jcr.resource.JcrResourceUtil;
 import org.apache.sling.jcr.resource.PathResolver;
 import org.apache.sling.jcr.resource.internal.helper.Descendable;
-import org.apache.sling.jcr.resource.internal.helper.JcrNodeResource;
-import org.apache.sling.jcr.resource.internal.helper.JcrNodeResourceIterator;
 import org.apache.sling.jcr.resource.internal.helper.Mapping;
 import org.apache.sling.jcr.resource.internal.helper.ResourcePathIterator;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProvider;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResourceIterator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -147,7 +148,8 @@
                 return ((Descendable) parent).listChildren();
             }
         } catch (SlingException se) {
-            log.warn("listChildren: Error trying to resolve parent resource " + parent.getURI(), se);
+            log.warn("listChildren: Error trying to resolve parent resource "
+                + parent.getURI(), se);
         }
 
         // return an empty iterator if parent has no node
@@ -158,7 +160,8 @@
     public Iterator<Resource> findResources(String query, String language)
             throws SlingException {
         try {
-            QueryResult res = JcrResourceUtil.query(getSession(), query, language);
+            QueryResult res = JcrResourceUtil.query(getSession(), query,
+                language);
             return new JcrNodeResourceIterator(this, res.getNodes());
         } catch (javax.jcr.query.InvalidQueryException iqe) {
             throw new SlingException(iqe);
@@ -170,7 +173,8 @@
     public Iterator<Map<String, Object>> queryResources(String query,
             String language) throws SlingException {
         try {
-            QueryResult result = JcrResourceUtil.query(getSession(), query, language);
+            QueryResult result = JcrResourceUtil.query(getSession(), query,
+                language);
             final String[] colNames = result.getColumnNames();
             final RowIterator rows = result.getRows();
             return new Iterator<Map<String, Object>>() {
@@ -207,7 +211,7 @@
 
     /**
      * @throws AccessControlException If an item would exist but is not readable
-     *      to this manager's session.
+     *             to this manager's session.
      */
     public Resource resolve(String uri) throws SlingException {
 
@@ -220,7 +224,6 @@
             log.error("Failed to decode request URI " + uri, e);
         }
 
-
         // resolve virtual uri
         String realUrl = factory.virtualToRealUri(uri);
         if (realUrl != null) {
@@ -322,10 +325,14 @@
         path = JcrResourceUtil.normalize(path);
         if (path != null) {
             try {
-                return getResourceInternal(path, type);
-            } catch (RepositoryException re) {
+                Resource resource = getResourceInternal(path);
+                if (type != null && resource instanceof JcrNodeResource) {
+                    ((JcrNodeResource) resource).setObjectType(type);
+                }
+                return resource;
+            } catch (Exception ex) {
                 throw new SlingException("Problem accessing resource" + path,
-                    re);
+                    ex);
             }
         }
 
@@ -359,9 +366,9 @@
                 getSession().getWorkspace().copy(source, destination);
             } else {
                 // TODO: Create node at destination:
-                //     - same primary node type
-                //     - same mixins
-                //     - same non-protected properties
+                // - same primary node type
+                // - same mixins
+                // - same non-protected properties
             }
 
         } catch (AccessControlException ace) {
@@ -461,8 +468,7 @@
      * {@link #DEFAULT_CONTENT_CLASS default content class}.
      *
      * @param type Load the node's content into an object of the given type if
-     *      not <code>null</code>.
-     *
+     *            not <code>null</code>.
      * @return the <code>Content</code> object loaded from the node or
      *         <code>null</code> if no node exists at the given path.
      */
@@ -501,7 +507,7 @@
         return null;
     }
 
-    protected Session getSession() {
+    public Session getSession() {
         return session;
     }
 
@@ -542,11 +548,11 @@
             final ResourcePathIterator it = new ResourcePathIterator(uriPath);
             while (it.hasNext() && resource == null) {
                 curPath = it.next();
-                resource = getResourceInternal(curPath, null);
+                resource = getResourceInternal(curPath);
             }
-        } catch (RepositoryException re) {
+        } catch (Exception ex) {
             throw new SlingException("Problem trying " + curPath
-                + " for request path " + uriPath, re);
+                + " for request path " + uriPath, ex);
         }
 
         return resource;
@@ -558,19 +564,21 @@
      * @throws AccessControlException If an item exists but this manager has no
      *             read access
      */
-    protected Resource getResourceInternal(String path, Class<?> type)
-            throws RepositoryException {
+    protected Resource getResourceInternal(String path) throws Exception {
 
-        // check JCR repository
-        if (itemExists(path)) {
-            Resource result = new JcrNodeResource(this, getSession(), path, type);
-            result.getResourceMetadata().put(ResourceMetadata.RESOLUTION_PATH,
-                path);
-            log.info("Found JCR Node Resource at path '{}'", path);
-            return result;
+        ResourceProvider rp = factory.getResourceProvider(path);
+        Resource resource = rp.getResource(this, path);
+        if (resource == null && rp != factory) {
+            resource = factory.getResource(this, path);
         }
 
-        log.debug("Path '{}' does not resolve to an Item", path);
+        if (resource != null) {
+            resource.getResourceMetadata().put(
+                ResourceMetadata.RESOLUTION_PATH, path);
+            return resource;
+        }
+
+        log.debug("Cannot resolve path '{}' to a resource", path);
         return null;
     }
 
@@ -587,7 +595,7 @@
      * @throws AccessControlException If the item really exists but this content
      *             manager's session has no read access to it.
      */
-    protected boolean itemExists(String path) throws RepositoryException {
+    public boolean itemExists(String path) throws RepositoryException {
         if (factory.itemReallyExists(getSession(), path)) {
             checkPermission(path, ACTION_READ);
             return true;
@@ -597,12 +605,11 @@
     }
 
     /**
-     *
      * @param path
      * @param actions
      * @throws RepositoryException
-     * @throws AccessControlException if this manager does not have the permission
-     * for the listed action(s).
+     * @throws AccessControlException if this manager does not have the
+     *             permission for the listed action(s).
      */
     protected void checkPermission(String path, String actions)
             throws RepositoryException {

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java?rev=601511&r1=601510&r2=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java Wed Dec  5 12:38:23 2007
@@ -18,13 +18,14 @@
  */
 package org.apache.sling.jcr.resource.internal;
 
+import static org.apache.sling.jcr.resource.JcrResourceConstants.BUNDLE_RESOURCE_ROOTS;
+
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -32,11 +33,16 @@
 import org.apache.commons.collections.BidiMap;
 import org.apache.commons.collections.bidimap.TreeBidiMap;
 import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceManager;
 import org.apache.sling.commons.mime.MimeTypeService;
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.resource.JcrResourceManagerFactory;
 import org.apache.sling.jcr.resource.internal.helper.Mapping;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProvider;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
+import org.apache.sling.jcr.resource.internal.helper.bundle.BundleResourceProvider;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource;
 import org.apache.sling.jcr.resource.internal.loader.Loader;
 import org.apache.sling.jcr.resource.internal.mapping.ObjectContentManagerFactory;
 import org.osgi.framework.Bundle;
@@ -69,7 +75,7 @@
  * @scr.service interface="org.apache.sling.jcr.resource.JcrResourceManagerFactory"
  */
 public class JcrResourceManagerFactoryImpl implements
-        JcrResourceManagerFactory, SynchronousBundleListener {
+        JcrResourceManagerFactory, SynchronousBundleListener, ResourceProvider {
 
     /**
      * @scr.property value="true" type="Boolean"
@@ -119,7 +125,10 @@
      */
     private MimeTypeService mimeTypeService;
 
-    /** This services ServiceReference for use in {@link #fireEvent(Bundle, String, Map)} */
+    /**
+     * This services ServiceReference for use in
+     * {@link #fireEvent(Bundle, String, Map)}
+     */
     private ServiceReference serviceReference;
 
     /** all mappings */
@@ -152,6 +161,14 @@
      */
     private Loader initialContentLoader;
 
+    private ResourceProviderEntry rootProviderEntry;
+
+    private Map<Long, BundleResourceProvider> bundleResourceProviderMap = new HashMap<Long, BundleResourceProvider>();
+
+    public JcrResourceManagerFactoryImpl() {
+        this.rootProviderEntry = new ResourceProviderEntry("/", this);
+    }
+
     // ---------- JcrResourceManagerFactory ------------------------------------
 
     /**
@@ -200,13 +217,13 @@
             case BundleEvent.STARTING: // STARTED:
                 // register mappings before the bundle gets activated
                 objectContentManagerFactory.registerMapperClient(event.getBundle());
-                addBundleResources(event.getBundle());
+                addBundleResourceProvider(event.getBundle());
                 break;
 
             case BundleEvent.STOPPED:
                 // remove mappings after the bundle has stopped
                 objectContentManagerFactory.unregisterMapperClient(event.getBundle());
-                removeBundleResources(event.getBundle());
+                removeBundleResourceProvider(event.getBundle());
                 break;
 
             case BundleEvent.UNINSTALLED:
@@ -215,6 +232,23 @@
         }
     }
 
+    // ---------- ResourceProvider ---------------------------------------------
+
+    public String[] getRoots() {
+        return new String[] { "/" };
+    }
+
+    public Resource getResource(JcrResourceManager jcrResourceManager,
+            String path) throws RepositoryException {
+
+        if (jcrResourceManager.itemExists(path)) {
+            log.info("getResource: Found JCR Node Resource at path '{}'", path);
+            return new JcrNodeResource(jcrResourceManager, path);
+        }
+
+        return null;
+    }
+
     // ---------- EventAdmin Event Dispatching ---------------------------------
 
     /**
@@ -313,45 +347,26 @@
 
     // ---------- Bundle provided resources -----------------------------------
 
-    private Map<String, Bundle> bundleResourcesByPrefix = new HashMap<String, Bundle>();
-    private Map<Long, String[]> bundleResourcesByBundleId = new HashMap<Long, String[]>();
-
-    private void addBundleResources(Bundle bundle) {
-        String prefixes = (String) bundle.getHeaders().get("Sling-Resource-Prefixes");
+    private void addBundleResourceProvider(Bundle bundle) {
+        String prefixes = (String) bundle.getHeaders().get(
+            BUNDLE_RESOURCE_ROOTS);
         if (prefixes != null) {
-            StringTokenizer pt = new StringTokenizer(prefixes, ", \t\n\r\f");
-            List<String> prefixList = new ArrayList<String>();
-            while (pt.hasMoreTokens()) {
-                String prefix = pt.nextToken().trim();
-                if (prefix.length() > 0) {
-                    bundleResourcesByPrefix.put(prefix, bundle);
-                    prefixList.add(prefix);
-                }
-            }
-            if (prefixList.size() > 0) {
-                bundleResourcesByBundleId.put(bundle.getBundleId(),
-                    prefixList.toArray(new String[prefixList.size()]));
-            }
+            BundleResourceProvider brp = new BundleResourceProvider(bundle,
+                prefixes);
+            rootProviderEntry.addResourceProvider(brp);
+            bundleResourceProviderMap.put(bundle.getBundleId(), brp);
         }
     }
 
-    private void removeBundleResources(Bundle bundle) {
-        String[] prefixes = bundleResourcesByBundleId.get(bundle.getBundleId());
-        if (prefixes != null) {
-            for (String prefix : prefixes) {
-                bundleResourcesByPrefix.remove(prefix);
-            }
+    private void removeBundleResourceProvider(Bundle bundle) {
+        BundleResourceProvider brp = bundleResourceProviderMap.get(bundle.getBundleId());
+        if (brp != null) {
+            rootProviderEntry.removeResourceProvider(brp);
         }
     }
 
-    Bundle getBundleForResource(String path) {
-        for (Map.Entry<String, Bundle> entry : bundleResourcesByPrefix.entrySet()) {
-            if (path.startsWith(entry.getKey())) {
-                return entry.getValue();
-            }
-        }
-
-        return null;
+    ResourceProvider getResourceProvider(String path) {
+        return rootProviderEntry.getResourceProvider(path);
     }
 
     // ---------- SCR Integration ---------------------------------------------
@@ -379,14 +394,13 @@
                 if (bundle.getState() == Bundle.ACTIVE) {
                     // register active bundles with the mapper client
                     objectContentManagerFactory.registerMapperClient(bundle);
-                    addBundleResources(bundle);
+                    addBundleResourceProvider(bundle);
                 }
             }
         } catch (Throwable t) {
             log.error("activate: Problem while loading initial content and"
                 + " registering mappings for existing bundles", t);
         }
-
 
         Dictionary<?, ?> properties = componentContext.getProperties();
 

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProvider.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProvider.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProvider.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProvider.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,53 @@
+/*
+ * 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.sling.api.resource.Resource;
+import org.apache.sling.jcr.resource.internal.JcrResourceManager;
+
+/**
+ * API for providers of resources. Used by the {@link JcrResourceManager} and
+ * {@link org.apache.sling.jcr.resource.internal.JcrResourceManagerFactoryImpl}
+ * classes to transparently access resources from different locations such as a
+ * JCR repository (the default) or OSGi bundles.
+ * <p>
+ * This is an internal interface not available outside this bundle. It refers to
+ * the internal {@link JcrResourceManager} class, which is also not visible from
+ * outside of this bundle.
+ */
+public interface ResourceProvider {
+
+    /**
+     * A list of absolute path prefixes of resources available through this
+     * provider.
+     */
+    String[] getRoots();
+
+    /**
+     * Returns a resource from this resource provider or <code>null</code> if
+     * the resource provider cannot find it. The path should have one of the
+     * {@link #getRoots()} strings as its prefix.
+     *
+     * @throws Exception may be thrown in case of any problem creating the
+     *             <code>Resource</code> instance.
+     */
+    Resource getResource(JcrResourceManager jcrResourceManager, String path)
+            throws Exception;
+
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,189 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class ResourceProviderEntry implements Comparable<ResourceProviderEntry> {
+
+    private final String path;
+
+    private final String prefix;
+
+    private final ResourceProvider provider;
+
+    private ResourceProviderEntry[] entries;
+
+    public ResourceProviderEntry(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.provider = provider;
+    }
+
+    public void addResourceProvider(ResourceProvider provider) {
+        String[] roots = provider.getRoots();
+        for (int i = 0; i < roots.length; i++) {
+            addResourceProvider(roots[i], provider);
+        }
+    }
+
+    public void removeResourceProvider(ResourceProvider provider) {
+        String[] roots = provider.getRoots();
+        for (int i = 0; i < roots.length; i++) {
+            removeResourceProvider(roots[i]);
+        }
+    }
+
+    public ResourceProvider getResourceProvider(String path) {
+        if (path.equals(this.path)) {
+            return provider;
+        } else if (match(path)) {
+            if (entries != null) {
+
+                // consider relative path for further checks
+                path = path.substring(this.prefix.length());
+
+                for (ResourceProviderEntry entry : entries) {
+                    ResourceProvider provider = entry.getResourceProvider(path);
+                    if (provider != null) {
+                        return provider;
+                    }
+                }
+            }
+
+            // no more specific provider, return mine
+            return provider;
+        }
+
+        // no match for my prefix, return null
+        return null;
+    }
+
+    public boolean match(String path) {
+        return path.startsWith(prefix);
+    }
+
+    // ---------- Comparable<ResourceProviderEntry> interface ------------------
+
+    public int compareTo(ResourceProviderEntry o) {
+        return prefix.compareTo(o.prefix);
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    private boolean addResourceProvider(String prefix, ResourceProvider provider) {
+        if (prefix.equals(this.path)) {
+            throw new IllegalStateException(
+                "ResourceProviderEntry for prefix already exists");
+        } else if (match(prefix)) {
+
+            // consider relative path for further checks
+            prefix = prefix.substring(this.prefix.length());
+
+            // check whether there is a better suited place
+            if (entries != null) {
+                for (int i = 0; i < entries.length; i++) {
+                    ResourceProviderEntry entry = entries[i];
+                    if (entry.addResourceProvider(prefix, provider)) {
+                        return true;
+                    } else if (entry.prefix.startsWith(prefix)
+                        && entry.prefix.charAt(prefix.length()) == '/') {
+                        ResourceProviderEntry newEntry = new ResourceProviderEntry(
+                            prefix, provider);
+                        newEntry.addResourceProvider(entry.path, entry.provider);
+                        entries[i] = newEntry;
+                        return true;
+                    }
+
+                }
+            }
+
+            // none found, so add it here
+            ResourceProviderEntry entry = new ResourceProviderEntry(prefix,
+                provider);
+            if (entries == null) {
+                entries = new ResourceProviderEntry[] { entry };
+            } else {
+                SortedSet<ResourceProviderEntry> set = new TreeSet<ResourceProviderEntry>();
+                set.addAll(Arrays.asList(entries));
+                set.add(entry);
+                entries = set.toArray(new ResourceProviderEntry[set.size()]);
+            }
+
+            return true;
+        }
+
+        // the prefix does not match this prefix
+        return false;
+    }
+
+    private boolean removeResourceProvider(String prefix) {
+        if (prefix.equals(path)) {
+            return true;
+        } else if (match(prefix)) {
+            // consider relative path for further checks
+            prefix = prefix.substring(this.prefix.length());
+
+            // check whether there is a better suited place
+            if (entries != null) {
+                for (int i = 0; i < entries.length; i++) {
+                    ResourceProviderEntry entry = entries[i];
+                    if (entry.removeResourceProvider(prefix)) {
+                        if (entries.length == 1) {
+                            entries = null;
+                        } else {
+                            int newEntriesLen = entries.length - 1;
+                            ResourceProviderEntry[] newEntries = new ResourceProviderEntry[newEntriesLen];
+                            if (i > 0) {
+                                System.arraycopy(entries, 0, newEntries, 0, i);
+                            }
+                            if (i < newEntriesLen) {
+                                System.arraycopy(entries, i + 1, newEntries, i,
+                                    newEntriesLen - i);
+                            }
+                            entries = newEntries;
+                        }
+
+                        // reinsert children
+                        ResourceProviderEntry[] children = entry.entries;
+                        if (children != null) {
+                            String pathPrefix = this.prefix + entry.prefix;
+                            for (ResourceProviderEntry child : children) {
+                                String path = pathPrefix + child.path;
+                                addResourceProvider(path, child.provider);
+                            }
+                        }
+
+                        return false;
+                    }
+
+                }
+            }
+        }
+
+        return false;
+    }
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResource.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResource.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResource.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResource.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,179 @@
+/*
+ * 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.bundle;
+
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+import static org.apache.jackrabbit.JcrConstants.NT_FOLDER;
+import static org.apache.sling.api.resource.ResourceMetadata.CREATION_TIME;
+import static org.apache.sling.api.resource.ResourceMetadata.MODIFICATION_TIME;
+import static org.apache.sling.api.resource.ResourceMetadata.RESOLUTION_PATH;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Iterator;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.jcr.resource.internal.helper.Descendable;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** A Resource that wraps a Bundle entry */
+public class BundleResource implements Resource, Descendable {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final Bundle bundle;
+
+    private final String path;
+
+    private URL url;
+
+    private final String resourceType;
+
+    private final ResourceMetadata metadata;
+
+    public static BundleResource getResource(Bundle bundle, String path) {
+
+        // if the entry has no trailing slash, try to with a trailing
+        // slash in case the entry would be a folder
+        if (!path.endsWith("/")) {
+            BundleResource br = getResource(bundle, path + "/");
+            if (br != null) {
+                return br;
+            }
+        }
+
+        // has trailing slash or not a folder, try path itself
+        URL entry = bundle.getEntry(path);
+        if (entry != null) {
+            return new BundleResource(bundle, path);
+        }
+
+        // the bundle does not contain the path
+        return null;
+    }
+
+    public BundleResource(Bundle bundle, String path) {
+        this.bundle = bundle;
+        this.path = path.endsWith("/")
+                ? path.substring(0, path.length() - 1)
+                : path;
+        this.resourceType = path.endsWith("/") ? NT_FOLDER : NT_FILE;
+
+        metadata = new ResourceMetadata();
+        metadata.put(RESOLUTION_PATH, path);
+        metadata.put(CREATION_TIME, bundle.getLastModified());
+        metadata.put(MODIFICATION_TIME, bundle.getLastModified());
+    }
+
+    public String getURI() {
+        return path;
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public ResourceMetadata getResourceMetadata() {
+        return metadata;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <Type> Type adaptTo(Class<Type> type) {
+        if (type == InputStream.class) {
+            return (Type) getInputStream(); // unchecked cast
+        } else if (type == URL.class) {
+            return (Type) getURL(); // unchecked cast
+        }
+
+        // fall back to nothing
+        return null;
+    }
+
+    public String toString() {
+        return "BundleResource, type=" + resourceType + ", path=" + path;
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    /**
+     * Returns a stream to the bundle entry if it is a file. Otherwise returns
+     * <code>null</code>.
+     */
+    private InputStream getInputStream() {
+        // implement this for files only
+        if (isFile()) {
+            try {
+                URL url = getURL();
+                if (url != null) {
+                    return url.openStream();
+                }
+            } catch (IOException ioe) {
+                log.error(
+                    "getInputStream: Cannot get input stream for " + this, ioe);
+            }
+        }
+
+        // otherwise there is no stream
+        return null;
+    }
+
+    private URL getURL() {
+        if (url == null) {
+            try {
+                url = new URL(BundleResourceURLStreamHandler.PROTOCOL, null,
+                    -1, path, new BundleResourceURLStreamHandler(bundle));
+            } catch (MalformedURLException mue) {
+                log.error("getURL: Cannot get URL for " + this, mue);
+            }
+        }
+
+        return url;
+    }
+
+    // ---------- Descendable interface ----------------------------------------
+
+    public Iterator<Resource> listChildren() {
+        return new BundleResourceIterator(this);
+    }
+
+    public Resource getDescendent(String relPath) {
+
+        // only consider folder resources for descendents
+        if (!isFile()) {
+            URL descendent = bundle.getEntry(path + relPath);
+            if (descendent != null) {
+                new BundleResource(bundle, descendent.getPath());
+            }
+        }
+
+        return null;
+    }
+
+    Bundle getBundle() {
+        return bundle;
+    }
+
+    boolean isFile() {
+        return NT_FILE.equals(getResourceType());
+    }
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceIterator.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceIterator.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceIterator.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceIterator.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,128 @@
+/*
+ * 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.bundle;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.sling.api.resource.Resource;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>BundleResourceIterator</code> class is a resource iterator, which
+ * returns resources for each Bundle entry of an underlying enumeration of
+ * Bundle entry paths.
+ */
+class BundleResourceIterator implements Iterator<Resource> {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /** Bundle providing the entry resources */
+    private Bundle bundle;
+
+    /** Underlying bundle entry path enumeration */
+    private Enumeration<String> entries;
+
+    /** The length of the parent entry path, see seek() */
+    private int prefixLength;
+
+    /** The prefetched next iterator entry, null at the end of iterating */
+    private Resource nextResult;
+
+    /**
+     * Creates an instance using the given parent bundle resource.
+     */
+    @SuppressWarnings("unchecked")
+    BundleResourceIterator(BundleResource parent) {
+
+        if (parent.isFile()) {
+
+            // if the parent is a file, the iterator is empty
+            this.bundle = null;
+            this.entries = null;
+            this.prefixLength = 0;
+            this.nextResult = null;
+
+        } else {
+            // trailing slash to enumerate children
+            String parentPath = parent.getURI() + "/";
+
+            this.bundle = parent.getBundle();
+            // unchecked cast
+            this.entries = parent.getBundle().getEntryPaths(parentPath);
+            this.prefixLength = parentPath.length();
+            this.nextResult = seek();
+        }
+    }
+
+    /** Returns true if there is another Resource available */
+    public boolean hasNext() {
+        return nextResult != null;
+    }
+
+    /** Returns the next resource in the iterator */
+    public Resource next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        Resource result = nextResult;
+        nextResult = seek();
+        return result;
+    }
+
+    /**
+     * Throws <code>UnsupportedOperationException</code> as this method is not
+     * supported by this implementation.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Scans the entry path enumeration looking for the next entry being a
+     * direct child of the parent resource.
+     */
+    private Resource seek() {
+        while (entries.hasMoreElements()) {
+            String entry = entries.nextElement();
+
+            // require leading slash
+            if (!entry.startsWith("/")) {
+                entry = "/" + entry;
+            }
+
+            int slash = entry.indexOf('/', prefixLength);
+            if (slash < 0 || slash == entry.length() - 1) {
+                log.debug("seek: Using entry {}", entry);
+                return new BundleResource(bundle, entry);
+            }
+
+            log.debug("seek: Ignoring entry {}", entry);
+        }
+
+        // no more results
+        log.debug("seek: No more nodes, iterator exhausted");
+        return null;
+    }
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceProvider.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceProvider.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceProvider.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceProvider.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,71 @@
+/*
+ * 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.bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.jcr.resource.internal.JcrResourceManager;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProvider;
+import org.osgi.framework.Bundle;
+
+public class BundleResourceProvider implements ResourceProvider {
+
+    /** The bundle providing the resources */
+    private final Bundle bundle;
+
+    /** The root paths */
+    private final String[] roots;
+
+    /**
+     * Creates Bundle resource provider accessing entries in the given Bundle an
+     * supporting resources below root paths given by the rootList which is a
+     * comma (and whitespace) separated list of absolute paths.
+     */
+    public BundleResourceProvider(Bundle bundle, String rootList) {
+        this.bundle = bundle;
+
+        StringTokenizer pt = new StringTokenizer(rootList, ", \t\n\r\f");
+        List<String> prefixList = new ArrayList<String>();
+        while (pt.hasMoreTokens()) {
+            String prefix = pt.nextToken().trim();
+            if (prefix.length() > 0) {
+                prefixList.add(prefix);
+            }
+        }
+        this.roots = prefixList.toArray(new String[prefixList.size()]);
+    }
+
+    /** Returns the root paths */
+    public String[] getRoots() {
+        return roots;
+    }
+
+    /**
+     * Returns a BundleResource for the path if such an entry exists in the
+     * bundle of this provider. The JcrResourceManager is ignored by this
+     * implementation.
+     */
+    public Resource getResource(JcrResourceManager jrm, String path) {
+        return BundleResource.getResource(bundle, path);
+    }
+
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLConnection.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLConnection.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLConnection.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLConnection.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,109 @@
+/*
+ * 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.bundle;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import org.osgi.framework.Bundle;
+
+/**
+ * A Bundle based <code>UIRLConnection</code> which uses the bundle's last
+ * modification time as the last modification time of the URL in contrast to the
+ * (Apache Felix) URLConnection used for the bundle entry, which always returns
+ * zero.
+ */
+public class BundleResourceURLConnection extends URLConnection {
+
+    /** The bundle owning the resource underlying the URLConnection */
+    private Bundle bundle;
+
+    /** The original URLConnection */
+    URLConnection delegatee;
+
+    protected BundleResourceURLConnection(Bundle bundle, URL url) {
+        super(url);
+
+        this.bundle = bundle;
+    }
+
+    /**
+     * Connects this URLConnection to access the data and metadata such as the
+     * content length, last modification time and content type.
+     */
+    public synchronized void connect() throws IOException {
+        if (!connected) {
+            String path = url.getPath();
+            URL url = bundle.getEntry(getURL().getPath());
+            if (url == null) {
+                throw new IOException("Cannot find entry " + path
+                    + " in bundle " + bundle);
+            }
+
+            delegatee = url.openConnection();
+
+            connected = true;
+        }
+    }
+
+    /** Returns the input stream of the Bundle provided URLConnection */
+    public InputStream getInputStream() throws IOException {
+        connect();
+
+        return delegatee.getInputStream();
+    }
+
+    /** Returns the content length of the Bundle provided URLConnection */
+    public int getContentLength() {
+        try {
+            connect();
+        } catch (IOException ex) {
+            return -1;
+        }
+
+        return delegatee.getContentLength();
+    }
+
+    /**
+     * Returns the last modification time of the underlying bundle, which is the
+     * last time the bundle was installed or updated
+     */
+    public long getLastModified() {
+        try {
+            connect();
+        } catch (IOException ex) {
+            return 0;
+        }
+
+        return bundle.getLastModified();
+    }
+
+    /** Returns the content type of the Bundle provided URLConnection */
+    public String getContentType() {
+        try {
+            connect();
+        } catch (IOException ex) {
+            return null;
+        }
+
+        return delegatee.getContentType();
+    }
+
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLStreamHandler.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLStreamHandler.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLStreamHandler.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/bundle/BundleResourceURLStreamHandler.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,48 @@
+/*
+ * 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.bundle;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import org.osgi.framework.Bundle;
+
+class BundleResourceURLStreamHandler extends URLStreamHandler {
+
+    static final String PROTOCOL = "bundle";
+
+    private final Bundle bundle;
+
+    BundleResourceURLStreamHandler(Bundle bundle) {
+        this.bundle = bundle;
+    }
+
+    @Override
+    protected URLConnection openConnection(URL u) throws IOException {
+        if (!PROTOCOL.equals(u.getProtocol())) {
+            throw new IOException("Cannot open connection to " + u
+                + ", wrong protocol");
+        }
+
+        return new BundleResourceURLConnection(bundle, u);
+    }
+
+}

Copied: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java (from r600523, incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResource.java)
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java?p2=incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java&p1=incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResource.java&r1=600523&r2=601511&rev=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResource.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java Wed Dec  5 12:38:23 2007
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.jcr.resource.internal.helper.jcr;
 
 import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
 import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
@@ -30,20 +30,17 @@
 import static org.apache.sling.api.resource.ResourceMetadata.RESOLUTION_PATH;
 import static org.apache.sling.jcr.resource.JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY;
 
-import java.io.IOException;
 import java.io.InputStream;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Iterator;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
 import org.apache.jackrabbit.net.URLFactory;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.jcr.resource.internal.JcrResourceManager;
+import org.apache.sling.jcr.resource.internal.helper.Descendable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -72,14 +69,13 @@
 
     private final ResourceMetadata metadata;
 
-    public JcrNodeResource(JcrResourceManager cMgr, Session s, String path,
-            Class<?> type) throws RepositoryException {
+    public JcrNodeResource(JcrResourceManager cMgr, String path)
+            throws RepositoryException {
         this.resourceManager = cMgr;
-        node = (Node) s.getItem(path);
+        node = (Node) cMgr.getSession().getItem(path);
         this.path = node.getPath();
         metadata = new ResourceMetadata();
         resourceType = getResourceTypeForNode(node);
-        objectType = type;
 
         // check for nt:file metadata
         setMetaData(node, metadata);
@@ -137,7 +133,7 @@
     Node getNode() {
         return node;
     }
-    
+
     // ---------- internal -----------------------------------------------------
 
     /**
@@ -178,7 +174,7 @@
         } catch (Exception ex) {
             log.error("getURL: Cannot create URL for " + this, ex);
         }
-        
+
         return null;
     }
 
@@ -203,6 +199,10 @@
                 + path, re);
             return null;
         }
+    }
+
+    public void setObjectType(Class<?> objectType) {
+        this.objectType = objectType;
     }
 
     /**

Copied: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java (from r600457, incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIterator.java)
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java?p2=incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java&p1=incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIterator.java&r1=600457&r2=601511&rev=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIterator.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java Wed Dec  5 12:38:23 2007
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.jcr.resource.internal.helper.jcr;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;

Modified: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIteratorTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIteratorTest.java?rev=601511&r1=601510&r2=601511&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIteratorTest.java (original)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/JcrNodeResourceIteratorTest.java Wed Dec  5 12:38:23 2007
@@ -27,6 +27,7 @@
 import junit.framework.TestCase;
 
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResourceIterator;
 import org.apache.sling.jcr.resource.testhelper.MockNode;
 import org.apache.sling.jcr.resource.testhelper.MockNodeIterator;
 

Added: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java?rev=601511&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java (added)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java Wed Dec  5 12:38:23 2007
@@ -0,0 +1,169 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.jcr.resource.internal.JcrResourceManager;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProvider;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
+
+public class ResourceProviderEntryTest extends TestCase {
+
+    private ResourceProvider rootProvider;
+
+    private ResourceProviderEntry root;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        rootProvider = new TestResourceProvider("/");
+        root = new ResourceProviderEntry("/", rootProvider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testRootProvider() {
+        assertNull(root.getResourceProvider("relpath"));
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(rootProvider, root.getResourceProvider("/rootel"));
+        assertEquals(rootProvider, root.getResourceProvider("/rootel/child"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/sample/html.js"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/microsling/html.js"));
+    }
+
+    public void testAdd1Provider() {
+        String firstPath = "/rootel";
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        root.addResourceProvider(first);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(first, root.getResourceProvider("/rootel"));
+        assertEquals(first, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(first, root.getResourceProvider("/rootel/child"));
+        assertEquals(first, root.getResourceProvider("/rootel/child/html.js"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/sample/html.js"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/microsling/html.js"));
+    }
+
+    public void testAdd3Providers() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+
+        root.addResourceProvider(first);
+        root.addResourceProvider(second);
+        root.addResourceProvider(third);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(first, root.getResourceProvider("/rootel"));
+        assertEquals(first, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(second, root.getResourceProvider("/rootel/child"));
+        assertEquals(second, root.getResourceProvider("/rootel/child/html.js"));
+        assertEquals(third,
+            root.getResourceProvider("/apps/sling/sample/html.js"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/microsling/html.js"));
+    }
+
+    public void testAdd3ProvidersReverse() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+
+        root.addResourceProvider(third);
+        root.addResourceProvider(second);
+        root.addResourceProvider(first);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(first, root.getResourceProvider("/rootel"));
+        assertEquals(first, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(second, root.getResourceProvider("/rootel/child"));
+        assertEquals(second, root.getResourceProvider("/rootel/child/html.js"));
+        assertEquals(third,
+            root.getResourceProvider("/apps/sling/sample/html.js"));
+        assertEquals(rootProvider,
+            root.getResourceProvider("/apps/sling/microsling/html.js"));
+    }
+
+    public void testRemoveProviders() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+
+        root.addResourceProvider(first);
+        root.addResourceProvider(second);
+        root.addResourceProvider(third);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(first, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(second, root.getResourceProvider("/rootel/child/html.js"));
+
+        root.removeResourceProvider(first);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(rootProvider, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(second, root.getResourceProvider("/rootel/child/html.js"));
+
+        root.addResourceProvider(first);
+
+        assertEquals(rootProvider, root.getResourceProvider("/"));
+        assertEquals(first, root.getResourceProvider("/rootel/html.js"));
+        assertEquals(second, root.getResourceProvider("/rootel/child/html.js"));
+    }
+
+    private static class TestResourceProvider implements ResourceProvider {
+
+        private final String[] roots;
+
+        TestResourceProvider(String root) {
+            roots = new String[] { root };
+        }
+
+        public Resource getResource(JcrResourceManager jcrResourceManager, String path) {
+            return null;
+        }
+
+        public String[] getRoots() {
+            return roots;
+        }
+
+    }
+}