You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2012/07/12 17:11:06 UTC

svn commit: r1360715 - in /sling/trunk/bundles: api/src/main/java/org/apache/sling/api/resource/ jcr/resource/src/main/java/org/apache/sling/jcr/resource/ jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/ jcr/resource/src/te...

Author: cziegeler
Date: Thu Jul 12 15:11:06 2012
New Revision: 1360715

URL: http://svn.apache.org/viewvc?rev=1360715&view=rev
Log:
SLING-2530 : Implement CRUD based on resources (WiP)

Added:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java   (with props)
Modified:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/AbstractResource.java
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/Resource.java
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
    sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
    sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderHandler.java
    sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java
    sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/AbstractResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/AbstractResource.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/AbstractResource.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/AbstractResource.java Thu Jul 12 15:11:06 2012
@@ -114,4 +114,32 @@ public abstract class AbstractResource
         //
         return ResourceUtil.isA(this, resourceType);
     }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#isModifiable()
+     */
+    public boolean isModifiable() {
+        return false;
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#remove()
+     */
+    public void remove() {
+        this.getResourceResolver().delete(this);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#update(org.apache.sling.api.resource.ValueMap)
+     */
+    public void update(final ValueMap properties) {
+        this.getResourceResolver().update(this, properties);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#addChild(java.lang.String, org.apache.sling.api.resource.ValueMap)
+     */
+    public Resource addChild(final String name, final ValueMap properties) {
+        return this.getResourceResolver().addChild(this, name, properties);
+    }
 }

Added: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java?rev=1360715&view=auto
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java (added)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java Thu Jul 12 15:11:06 2012
@@ -0,0 +1,49 @@
+/*
+ * 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.api.resource;
+
+
+
+/**
+ * A modifying resource provider is an extension of a resource provider which
+ * is only supported if the resource provider has been created through
+ * a {@link ResourceProviderFactory}.
+ *
+ * A modifying resource provider allows to create, update, and delete
+ * resources.
+ *
+ * TODO - Exception handling, return values
+ *
+ * @see ResourceProviderFactory#getResourceProvider(java.util.Map)
+ * @see ResourceProviderFactory#getAdministrativeResourceProvider(java.util.Map)
+ *
+ * @since 2.2.0
+ */
+public interface ModifyingResourceProvider {
+
+    Resource create(ResourceResolver resolver, String path, ValueMap properties);
+
+    boolean delete(ResourceResolver resolver, String path);
+
+    void update(ResourceResolver resolver, String path, ValueMap properties);
+
+    void revert();
+
+    void commit();
+}

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java Thu Jul 12 15:11:06 2012
@@ -30,6 +30,7 @@ import java.util.Map;
  * returned through a {@link ResourceProviderFactory}.
  *
  * TODO - what should the resource provider do, if the language is not supported?
+ * TODO - is returning null allowed?
  *
  * @since 2.2.0
  */

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/Resource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/Resource.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/Resource.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/Resource.java Thu Jul 12 15:11:06 2012
@@ -140,4 +140,27 @@ public interface Resource extends Adapta
      * retrieved.
      */
     ResourceResolver getResourceResolver();
+
+    /**
+     * @since 2.2.0
+     */
+    boolean isModifiable();
+
+    /**
+     * @since 2.2.0
+     * @throws UnsupportedOperationException
+     */
+    void remove();
+
+    /**
+     * @since 2.2.0
+     * @throws UnsupportedOperationException
+     */
+    void update(final ValueMap properties);
+
+    /**
+     * @since 2.2.0
+     * @throws UnsupportedOperationException
+     */
+    Resource addChild(final String name, final ValueMap properties);
 }

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java Thu Jul 12 15:11:06 2012
@@ -500,4 +500,14 @@ public interface ResourceResolver extend
      *             {@link #close() closed}.
      */
     Object getAttribute(String name);
+
+    boolean delete(Resource resource);
+
+    Resource addChild(Resource parent, String name, ValueMap properties);
+
+    void update(Resource resource, ValueMap properties);
+
+    void revert();
+
+    void commit();
 }

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java Thu Jul 12 15:11:06 2012
@@ -35,7 +35,7 @@ public class ResourceWrapper implements 
      * Creates a new wrapper instance delegating all method calls to the given
      * <code>resource</code>.
      */
-    public ResourceWrapper(Resource resource) {
+    public ResourceWrapper(final Resource resource) {
         this.resource = resource;
     }
 
@@ -158,4 +158,31 @@ public class ResourceWrapper implements 
             + ", path=" + getPath() + ", resource=[" + getResource() + "]";
     }
 
+    /**
+     * @see org.apache.sling.api.resource.Resource#isModifiable()
+     */
+    public boolean isModifiable() {
+        return getResource().isModifiable();
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#remove()
+     */
+    public void remove() {
+        getResource().remove();
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#update(org.apache.sling.api.resource.ValueMap)
+     */
+    public void update(final ValueMap properties) {
+        getResource().update(properties);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#addChild(java.lang.String, org.apache.sling.api.resource.ValueMap)
+     */
+    public Resource addChild(final String name, final ValueMap properties) {
+        return getResource().addChild(name, properties);
+    }
 }

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java Thu Jul 12 15:11:06 2012
@@ -19,6 +19,7 @@
 package org.apache.sling.jcr.resource;
 
 import java.io.InputStream;
+import java.math.BigDecimal;
 import java.util.Calendar;
 import java.util.StringTokenizer;
 
@@ -122,6 +123,8 @@ public class JcrResourceUtil {
             val = fac.createValue(fac.createBinary((InputStream)value));
         } else if (value instanceof Node) {
             val = fac.createValue((Node)value);
+        } else if (value instanceof BigDecimal) {
+            val = fac.createValue((BigDecimal)value);
         } else if (value instanceof Long) {
             val = fac.createValue((Long)value);
         } else if (value instanceof Number) {

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java Thu Jul 12 15:11:06 2012
@@ -41,12 +41,14 @@ import org.apache.sling.api.SlingExcepti
 import org.apache.sling.api.adapter.SlingAdaptable;
 import org.apache.sling.api.resource.AttributableResourceProvider;
 import org.apache.sling.api.resource.DynamicResourceProvider;
+import org.apache.sling.api.resource.ModifyingResourceProvider;
 import org.apache.sling.api.resource.QueriableResourceProvider;
 import org.apache.sling.api.resource.QuerySyntaxException;
 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.ResourceResolverFactory;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.jcr.resource.JcrResourceUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -62,7 +64,8 @@ public class JcrResourceProvider
     implements ResourceProvider,
                DynamicResourceProvider,
                AttributableResourceProvider,
-               QueriableResourceProvider {
+               QueriableResourceProvider,
+               ModifyingResourceProvider {
 
     /** column name for node path */
     private static final String QUERY_COLUMN_PATH = "jcr:path";
@@ -373,4 +376,90 @@ public class JcrResourceProvider
         }
         return super.adaptTo(type);
     }
+
+    /**
+     * @see org.apache.sling.api.resource.ModifyingResourceProvider#create(ResourceResolver, java.lang.String, org.apache.sling.api.resource.ValueMap)
+     */
+    public Resource create(final ResourceResolver resolver, final String path, final ValueMap properties) {
+        // check for node type
+        final String nodeType = (properties != null ? properties.get("jcr:primaryType", String.class) : null);
+        try {
+            final Node node = JcrResourceUtil.createPath(path, null, nodeType, this.session, false);
+
+            // mixin types
+            final String[] mixinTypes = (properties != null ? properties.get("jcr:mixinTypes", String[].class) : null);
+            if ( mixinTypes != null ) {
+                for(final String mixin : mixinTypes ) {
+                    if ( !node.isNodeType(mixin) ) {
+                        node.addMixin(mixin);
+                    }
+                }
+            }
+            this.update(node, properties);
+
+            return new JcrNodeResource(resolver, node, this.dynamicClassLoader);
+        } catch (final RepositoryException e) {
+            log.error("Unable to create node at " + path, e);
+        }
+        return null;
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ModifyingResourceProvider#delete(ResourceResolver, java.lang.String)
+     */
+    public boolean delete(final ResourceResolver resolver, final String path) {
+        try {
+            if ( session.itemExists(path) ) {
+                session.getItem(path).remove();
+                return true;
+            }
+        } catch (final RepositoryException e) {
+            log.error("Unable to delete item at " + path, e);
+        }
+        return false;
+    }
+
+    private void update(final Node node, final ValueMap properties) throws RepositoryException {
+        for(final Map.Entry<String, Object> entry : properties.entrySet()) {
+            if ( !entry.getKey().equals("jcr:primaryType") && !entry.getKey().equals("jcr:mixinTypes") ) {
+                JcrResourceUtil.setProperty(node, entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ModifyingResourceProvider#update(org.apache.sling.api.resource.ResourceResolver, java.lang.String, org.apache.sling.api.resource.ValueMap)
+     */
+    public void update(final ResourceResolver resolver, final String path, final ValueMap properties) {
+        try {
+            final Node node = this.session.getNode(path);
+
+            this.update(node, properties);
+
+        } catch (final RepositoryException e) {
+            log.error("Unable to update node at " + path, e);
+        }
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ModifyingResourceProvider#revert()
+     */
+    public void revert() {
+        try {
+            this.session.refresh(false);
+        } catch (final RepositoryException e) {
+            log.error("Unable to refresh session.", e);
+        }
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ModifyingResourceProvider#commit()
+     */
+    public void commit() {
+        try {
+            this.session.save();
+        } catch (final RepositoryException e) {
+            log.error("Unable to commit changes to session.", e);
+        }
+    }
 }

Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java Thu Jul 12 15:11:06 2012
@@ -32,6 +32,7 @@ import org.apache.sling.api.resource.Log
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.commons.testing.jcr.EventHelper;
 import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
 import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResource;
@@ -202,6 +203,30 @@ public class JcrResourceListenerTest ext
                 return null;
             }
 
+            public boolean delete(Resource resource) {
+                // TODO Auto-generated method stub
+                return false;
+            }
+
+            public Resource addChild(Resource parent, String name, ValueMap properties) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            public void update(Resource resource, ValueMap properties) {
+                // TODO Auto-generated method stub
+
+            }
+
+            public void revert() {
+                // TODO Auto-generated method stub
+
+            }
+
+            public void commit() {
+                // TODO Auto-generated method stub
+
+            }
         };
         final ResourceResolverFactory factory = new ResourceResolverFactory() {
 

Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java Thu Jul 12 15:11:06 2012
@@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletReq
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
 
 public class MockResourceResolver implements ResourceResolver {
 
@@ -125,4 +126,29 @@ public class MockResourceResolver implem
     public Iterator<String> getAttributeNames() {
         return Collections.<String> emptyList().iterator();
     }
+
+    public boolean delete(Resource resource) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public Resource addChild(Resource parent, String name, ValueMap properties) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public void update(Resource resource, ValueMap properties) {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void revert() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void commit() {
+        // TODO Auto-generated method stub
+
+    }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java Thu Jul 12 15:11:06 2012
@@ -37,6 +37,7 @@ import org.apache.sling.adapter.annotati
 import org.apache.sling.api.SlingException;
 import org.apache.sling.api.adapter.SlingAdaptable;
 import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ModifyingResourceProvider;
 import org.apache.sling.api.resource.NonExistingResource;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceNotFoundException;
@@ -994,4 +995,51 @@ public class ResourceResolverImpl extend
 
         return absPath;
     }
+
+    /**
+     * @see org.apache.sling.api.resource.ResourceResolver#delete(org.apache.sling.api.resource.Resource)
+     */
+    public boolean delete(final Resource resource) {
+        final String path = resource.getPath();
+        final ModifyingResourceProvider mrp = this.factory.getRootProviderEntry().getModifyingProvider(this.context,
+                        this,
+                        path);
+        return mrp.delete(this, path);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ResourceResolver#addChild(org.apache.sling.api.resource.Resource, java.lang.String, org.apache.sling.api.resource.ValueMap)
+     */
+    public Resource addChild(final Resource parent, final String name, final ValueMap properties) {
+        final String path = parent.getPath() + '/' + name;
+        final ModifyingResourceProvider mrp = this.factory.getRootProviderEntry().getModifyingProvider(this.context,
+                        this,
+                        path);
+        return mrp.create(this, path, properties);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ResourceResolver#update(org.apache.sling.api.resource.Resource, org.apache.sling.api.resource.ValueMap)
+     */
+    public void update(final Resource resource, final ValueMap properties) {
+        final String path = resource.getPath();
+        final ModifyingResourceProvider mrp = this.factory.getRootProviderEntry().getModifyingProvider(this.context,
+                        this,
+                        path);
+        mrp.update(this, path, properties);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ResourceResolver#revert()
+     */
+    public void revert() {
+        this.context.revert();
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.ResourceResolver#commit()
+     */
+    public void commit() {
+        this.context.commit();
+    }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java Thu Jul 12 15:11:06 2012
@@ -276,7 +276,7 @@ public class ResourceIterator implements
                     final Set<ProviderHandler> providers) {
 
         // collect providers along the ancestor path segements
-        final String[] elements = ResourceProviderEntry.split(path, '/');
+        final String[] elements = ResourceProviderEntry.split(path);
         ResourceProviderEntry base = rootProviderEntry;
         for (final String element : elements) {
             if (base.containsKey(element)) {

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java Thu Jul 12 15:11:06 2012
@@ -23,6 +23,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.sling.api.resource.DynamicResourceProvider;
+import org.apache.sling.api.resource.ModifyingResourceProvider;
 import org.apache.sling.api.resource.ResourceProvider;
 
 /**
@@ -38,6 +39,9 @@ public class ResourceResolverContext {
     /** A set of all dynamic providers (for closing them later on) */
     private final Set<DynamicResourceProvider> dynamicProviders = new HashSet<DynamicResourceProvider>();
 
+    /** A set of all modifying providers */
+    private final Set<ModifyingResourceProvider> modifyingProviders = new HashSet<ModifyingResourceProvider>();
+
     /** Is this a resource resolver for an admin? */
     private final boolean isAdmin;
 
@@ -78,6 +82,9 @@ public class ResourceResolverContext {
         if (provider instanceof DynamicResourceProvider) {
             this.dynamicProviders.add((DynamicResourceProvider) provider);
         }
+        if (provider instanceof ModifyingResourceProvider) {
+            this.modifyingProviders.add((ModifyingResourceProvider) provider);
+        }
     }
 
     /**
@@ -113,4 +120,22 @@ public class ResourceResolverContext {
         this.dynamicProviders.clear();
         this.providers.clear();
     }
+
+    /**
+     * Revert all transient changes.
+     */
+    public void revert() {
+        for(final ModifyingResourceProvider provider : this.modifyingProviders) {
+            provider.revert();
+        }
+    }
+
+    /**
+     * Commit all transient changes
+     */
+    public void commit() {
+        for(final ModifyingResourceProvider provider : this.modifyingProviders) {
+            provider.commit();
+        }
+    }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java Thu Jul 12 15:11:06 2012
@@ -142,6 +142,8 @@ public abstract class ProviderHandler im
      */
     public abstract Iterator<Resource> listChildren(final ResourceResolverContext ctx, final Resource parent);
 
+    public abstract ResourceProvider getResourceProvider(final ResourceResolverContext ctx);
+
     /**
      * Return a name of the resource provider/factory.
      */

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java Thu Jul 12 15:11:06 2012
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.commons.collections.FastTreeMap;
+import org.apache.sling.api.resource.ModifyingResourceProvider;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
@@ -116,32 +117,12 @@ public class ResourceProviderEntry imple
      * @throws org.apache.sling.api.SlingException
      *             if an error occurrs trying to access an existing resource.
      */
-    public Resource getResource(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String path) {
+    public Resource getResource(final ResourceResolverContext ctx,
+                    final ResourceResolver resourceResolver,
+                    final String path) {
         return getInternalResource(ctx, resourceResolver, path);
     }
 
-    /**
-     * 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 synchronized boolean addResourceProvider(final String prefix, final ProviderHandler provider) {
-        final String[] elements = split(prefix, '/');
-        final List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
-        entryPath.add(this); // add this the start so if the list is empty
-        // we have a position to add to
-        populateProviderPath(entryPath, elements);
-        for (int i = entryPath.size() - 1; i < elements.length; i++) {
-            final String stubPrefix = elements[i];
-            final ResourceProviderEntry rpe2 = new ResourceProviderEntry(stubPrefix, new ProviderHandler[0]);
-            entryPath.get(i).put(stubPrefix, rpe2);
-            entryPath.add(rpe2);
-        }
-        return entryPath.get(elements.length).addInternalProvider(provider);
-    }
-
     // ------------------ Map methods, here so that we can delegate 2 maps
     // together
     @SuppressWarnings("unchecked")
@@ -164,19 +145,9 @@ public class ResourceProviderEntry imple
         return storageMapValues;
     }
 
-    public synchronized boolean removeResourceProvider(final String prefix, final ProviderHandler resourceProvider) {
-        final String[] elements = split(prefix, '/');
-        final List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
-        populateProviderPath(entryPath, elements);
-        if (entryPath.size() > 0 && entryPath.size() == elements.length) {
-            // the last element is a perfect match;
-            return entryPath.get(entryPath.size() - 1).removeInternalProvider(resourceProvider);
-        }
-        return false;
-    }
-
-    // ---------- Comparable<ResourceProviderEntry> interface ------------------
-
+    /**
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
     public int compareTo(final ResourceProviderEntry o) {
         return prefix.compareTo(o.prefix);
     }
@@ -191,9 +162,8 @@ public class ResourceProviderEntry imple
     private boolean addInternalProvider(final ProviderHandler provider) {
         final int before = providers.length;
         final Set<ProviderHandler> set = new HashSet<ProviderHandler>();
-        if (providers != null) {
-            set.addAll(Arrays.asList(providers));
-        }
+        set.addAll(Arrays.asList(providers));
+
         LOGGER.debug("Adding provider {} at {} ", provider, path);
         set.add(provider);
         providers = conditionalSort(set);
@@ -208,15 +178,54 @@ public class ResourceProviderEntry imple
     private boolean removeInternalProvider(final ProviderHandler provider) {
         final int before = providers.length;
         final Set<ProviderHandler> set = new HashSet<ProviderHandler>();
-        if (providers != null) {
-            set.addAll(Arrays.asList(providers));
-        }
+        set.addAll(Arrays.asList(providers));
+
+        LOGGER.debug("Removing provider {} at {} ", provider, path);
         set.remove(provider);
         providers = conditionalSort(set);
         return providers.length < before;
     }
 
     /**
+     * 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.
+     */
+    protected synchronized boolean addResourceProvider(final String prefix, final ProviderHandler provider) {
+        final String[] elements = split(prefix);
+        final List<ResourceProviderEntry> entries = new ArrayList<ResourceProviderEntry>();
+        this.populateProviderPath(entries, elements);
+
+        // add this=root to the start so if the list is empty
+        // we have a position to add to
+        entries.add(0, this);
+        for (int i = entries.size() - 1; i < elements.length; i++) {
+            final String stubPrefix = elements[i];
+            final ResourceProviderEntry rpe2 = new ResourceProviderEntry(stubPrefix, new ProviderHandler[0]);
+            entries.get(i).put(stubPrefix, rpe2);
+            entries.add(rpe2);
+        }
+        return entries.get(elements.length).addInternalProvider(provider);
+    }
+
+    /**
+     * Remove the given resource provider from the tree
+     */
+    protected synchronized boolean removeResourceProvider(final String prefix, final ProviderHandler resourceProvider) {
+        final String[] elements = split(prefix);
+        final List<ResourceProviderEntry> entries = new ArrayList<ResourceProviderEntry>();
+        this.populateProviderPath(entries, elements);
+
+        if (entries.size() > 0 && entries.size() == elements.length) {
+            // the last element is a perfect match;
+            return entries.get(entries.size() - 1).removeInternalProvider(resourceProvider);
+        }
+        return false;
+    }
+
+    /**
      * Return a sorted array of handlers.
      */
     private ProviderHandler[] conditionalSort(final Set<ProviderHandler> set) {
@@ -229,23 +238,19 @@ public class ResourceProviderEntry imple
     }
 
     /**
-     * Get a of ResourceProvidersEntries leading to the fullPath in reverse
-     * order.
-     *
-     * @param fullPath
-     *            the full path
+     * Get a list of resource provider entries in reverse order.
+     * @param entries List to add the entries to
+     * @param elements The path already split into segments.
      */
-    private void populateProviderPath(final List<ResourceProviderEntry> providerEntryPath, final String[] elements) {
+    private void populateProviderPath(final List<ResourceProviderEntry> entries, final String[] elements) {
         ResourceProviderEntry base = this;
-        if (elements != null) {
-            for (final String element : elements) {
-                if (element != null) {
-                    if (base.containsKey(element)) {
-                        base = base.get(element);
-                        providerEntryPath.add(base);
-                    } else {
-                        break;
-                    }
+        for (final String element : elements) {
+            if (element != null) {
+                if (base.containsKey(element)) {
+                    base = base.get(element);
+                    entries.add(base);
+                } else {
+                    break;
                 }
             }
         }
@@ -254,28 +259,27 @@ public class ResourceProviderEntry imple
     /**
      * Resolve a resource from a path into a Resource
      *
-     * @param resolver
-     *            the ResourceResolver.
-     * @param fullPath
-     *            the Full path
+     * @param ctx The resource resolver context
+     * @param resourceResolver the ResourceResolver.
+     * @param fullPath the Full path
      * @return null if no resource was found, a resource if one was found.
      */
-    private Resource getInternalResource(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String fullPath) {
-        final long start = System.currentTimeMillis();
+    private Resource getInternalResource(final ResourceResolverContext ctx,
+                    final ResourceResolver resourceResolver,
+                    final String fullPath) {
         try {
 
             if (fullPath == null || fullPath.length() == 0 || fullPath.charAt(0) != '/') {
-                LOGGER.debug("Not absolute {} :{}", fullPath, (System.currentTimeMillis() - start));
+                LOGGER.debug("Not absolute {}", fullPath);
                 return null; // fullpath must be absolute
             }
-            final String[] elements = split(fullPath, '/');
+            final String[] elements = split(fullPath);
+            final List<ResourceProviderEntry> entries = new ArrayList<ResourceProviderEntry>();
+            this.populateProviderPath(entries, elements);
 
-            final List<ResourceProviderEntry> list = new ArrayList<ResourceProviderEntry>();
-            populateProviderPath(list, elements);
             // the path is in reverse order end first
-
-            for (int i = list.size() - 1; i >= 0; i--) {
-                final ProviderHandler[] rps = list.get(i).getResourceProviders();
+            for (int i = entries.size() - 1; i >= 0; i--) {
+                final ProviderHandler[] rps = entries.get(i).getResourceProviders();
                 for (final ProviderHandler rp : rps) {
 
                     final Resource resource = rp.getResource(ctx, resourceResolver, fullPath);
@@ -284,6 +288,7 @@ public class ResourceProviderEntry imple
                         return resource;
                     }
                 }
+                // TODO stop handling if provider claims subtree!
             }
 
             // resolve against this one
@@ -296,8 +301,8 @@ public class ResourceProviderEntry imple
             // 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
-            if (list.size() > 0 && list.size() == elements.length) {
-                if (list.get(list.size() - 1).getResourceProviders().length == 0) {
+            if (entries.size() > 0 && entries.size() == elements.length) {
+                if (entries.get(entries.size() - 1).getResourceProviders().length == 0) {
                     LOGGER.debug("Resolved Synthetic {}", fullPath);
                     return new SyntheticResource(resourceResolver, fullPath, ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
                 }
@@ -311,7 +316,8 @@ public class ResourceProviderEntry imple
         }
     }
 
-    public Resource getResourceFromProviders(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String fullPath) {
+    public Resource getResourceFromProviders(final ResourceResolverContext ctx,
+                    final ResourceResolver resourceResolver, final String fullPath) {
         final ProviderHandler[] rps = getResourceProviders();
         for (final ProviderHandler rp : rps) {
             final Resource resource = rp.getResource(ctx, resourceResolver, fullPath);
@@ -323,32 +329,56 @@ public class ResourceProviderEntry imple
         return null;
     }
 
+    public ModifyingResourceProvider getModifyingProvider(final ResourceResolverContext ctx,
+                    final ResourceResolver resourceResolver,
+                    final String fullPath) {
+        final String[] elements = split(fullPath);
+        final List<ResourceProviderEntry> entries = new ArrayList<ResourceProviderEntry>();
+        this.populateProviderPath(entries, elements);
+
+        for (int i = entries.size() - 1; i >= 0; i--) {
+            final ProviderHandler[] rps = entries.get(i).getResourceProviders();
+            for (final ProviderHandler rp : rps) {
+                final ResourceProvider provider = rp.getResourceProvider(ctx);
+                if ( provider instanceof ModifyingResourceProvider ) {
+                    return (ModifyingResourceProvider) provider;
+                }
+            }
+            // TODO stop handling if provider claims subtree!
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    private static final char SPLIT_SEP = '/';
+    private static final String[] EMPTY_RESULT = new String[0];
+
     /**
-     * @param st
-     * @param sep
+     * Split the string by slash.
+     * This method never returns null.
+     * @param st The string to split
      * @return an array of the strings between the separator
      */
-    public static String[] split(final String st, final char sep) {
+    public static String[] split(final String st) {
 
         if (st == null) {
-            return new String[0];
+            return EMPTY_RESULT;
         }
         final char[] pn = st.toCharArray();
         if (pn.length == 0) {
-            return new String[0];
+            return EMPTY_RESULT;
         }
-        if (pn.length == 1 && pn[0] == sep) {
-            return new String[0];
+        if (pn.length == 1 && pn[0] == SPLIT_SEP) {
+            return EMPTY_RESULT;
         }
         int n = 1;
         int start = 0;
         int end = pn.length;
-        while (start < end && sep == pn[start])
+        while (start < end && SPLIT_SEP == pn[start])
             start++;
-        while (start < end && sep == pn[end - 1])
+        while (start < end && SPLIT_SEP == pn[end - 1])
             end--;
         for (int i = start; i < end; i++) {
-            if (sep == pn[i]) {
+            if (SPLIT_SEP == pn[i]) {
                 n++;
             }
         }
@@ -356,7 +386,7 @@ public class ResourceProviderEntry imple
         int s = start;
         int j = 0;
         for (int i = start; i < end; i++) {
-            if (pn[i] == sep) {
+            if (pn[i] == SPLIT_SEP) {
                 e[j++] = new String(pn, s, i - s);
                 s = i + 1;
             }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderHandler.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderHandler.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderHandler.java Thu Jul 12 15:11:06 2012
@@ -65,6 +65,13 @@ public class ResourceProviderHandler ext
     }
 
     /**
+     * @see org.apache.sling.resourceresolver.impl.tree.ProviderHandler#getResourceProvider(org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext)
+     */
+    public ResourceProvider getResourceProvider(final ResourceResolverContext ctx) {
+        return this.getResourceProvider();
+    }
+
+    /**
      * {@inheritDoc}
      * @see java.lang.Object#hashCode()
      */

Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java (original)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java Thu Jul 12 15:11:06 2012
@@ -94,5 +94,11 @@ public class ProviderHandlerTest {
             return null;
         }
 
+        @Override
+        public ResourceProvider getResourceProvider(ResourceResolverContext ctx) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
     }
 }

Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java?rev=1360715&r1=1360714&r2=1360715&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java (original)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java Thu Jul 12 15:11:06 2012
@@ -34,9 +34,7 @@ import org.apache.sling.api.resource.Res
 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.resourceresolver.impl.tree.ProviderHandler;
-import org.apache.sling.resourceresolver.impl.tree.ResourceProviderEntry;
-import org.apache.sling.resourceresolver.impl.tree.ResourceProviderHandler;
+import org.apache.sling.api.resource.ValueMap;
 import org.junit.Before;
 import org.junit.Test;
 import org.osgi.framework.Constants;
@@ -301,6 +299,30 @@ public class ResourceProviderEntryTest {
         public Iterator<String> getAttributeNames() {
             return Collections.<String> emptyList().iterator();
         }
+
+        public boolean delete(Resource resource) {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        public Resource addChild(Resource parent, String name, ValueMap properties) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public void update(Resource resource, ValueMap properties) {
+            // TODO Auto-generated method stub
+        }
+
+        public void revert() {
+            // TODO Auto-generated method stub
+
+        }
+
+        public void commit() {
+            // TODO Auto-generated method stub
+
+        }
     }
 
     private static class TestResource extends AbstractResource {