Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParametersParser.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParametersParser.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParametersParser.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParametersParser.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,159 @@
+/*
+ * 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.resourceresolver.impl.params;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class ParametersParser {
+
+    private enum ParamsState {
+        INIT, NAME, EQUALS, VALUE, QUOTED_VALUE, QUOTE_END
+    }
+
+    private StringBuilder name;
+
+    private StringBuilder value;
+
+    private Map<String, String> parameters = new LinkedHashMap<String, 
String>();
+
+    private boolean invalid;
+
+    /**
+     * Parses parameters string, eg.: {@code ;x=123;a='1.0'}. The result of 
the method is available in
+     * {@link #parameters} and {@link #invalid}.
+     * 
+     * @param chars Array containing path with parameters.
+     * @param from Index of the first character of the parameters substring 
(it must be a semicolon).
+     * @param dotAllowed If true, the dot in parameter value won't stop 
parsing.
+     * @return Index of the first character not related to parameters.
+     */
+    public int parseParameters(final char[] chars, final int from, final 
boolean dotAllowed) {
+        resetCurrentParameter();
+        parameters.clear();
+        invalid = false;
+
+        ParamsState state = ParamsState.INIT;
+        for (int i = from; i <= chars.length; i++) {
+            final char c;
+            if (i == chars.length) {
+                c = 0;
+            } else {
+                c = chars[i];
+            }
+            switch (state) {
+                case INIT:
+                    if (c == ';') {
+                        state = ParamsState.NAME;
+                    } else if (c == '.' || c == '/' || c == 0) {
+                        invalid = true;
+                        return i;
+                    }
+                    break;
+
+                case NAME:
+                    if (c == '=') {
+                        state = ParamsState.EQUALS;
+                    } else if (c == '.' || c == '/' || c == 0) {
+                        invalid = true;
+                        return i;
+                    } else if (c == ';') {
+                        resetCurrentParameter();
+                    } else {
+                        name.append(c);
+                    }
+                    break;
+
+                case EQUALS:
+                    if (c == '\'') {
+                        state = ParamsState.QUOTED_VALUE;
+                    } else if (c == '.' || c == '/' || c == 0) {
+                        addParameter(); // empty one
+                        return i;
+                    } else if (c == ';') {
+                        state = ParamsState.NAME; // empty one
+                        addParameter();
+                    } else {
+                        state = ParamsState.VALUE;
+                        value.append(c);
+                    }
+                    break;
+
+                case QUOTED_VALUE:
+                    if (c == '\'') {
+                        state = ParamsState.QUOTE_END;
+                        addParameter();
+                    } else if (c == 0) {
+                        invalid = true;
+                        return i;
+                    } else {
+                        value.append(c);
+                    }
+                    break;
+
+                case VALUE:
+                    if (c == ';') {
+                        state = ParamsState.NAME;
+                        addParameter();
+                    } else if ((c == '.' && !dotAllowed) || c == '/' || c == 
0) {
+                        addParameter();
+                        return i;
+                    } else {
+                        value.append(c);
+                    }
+                    break;
+
+                case QUOTE_END:
+                    if (c == ';') {
+                        state = ParamsState.NAME;
+                    } else {
+                        return i;
+                    }
+            }
+        }
+
+        return chars.length;
+    }
+
+    /**
+     * @return Parsed parameters.
+     */
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+    /**
+     * @return True if the {@link #parseParameters(char[], int, boolean)} 
method failed.
+     */
+    public boolean isInvalid() {
+        return invalid;
+    }
+
+    private void resetCurrentParameter() {
+        name = new StringBuilder();
+        value = new StringBuilder();
+    }
+
+    private void addParameter() {
+        parameters.put(name.toString(), value.toString());
+        name = new StringBuilder();
+        value = new StringBuilder();
+    }
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParsedParameters.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParsedParameters.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParsedParameters.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/ParsedParameters.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,75 @@
+/*
+ * 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 SF 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.resourceresolver.impl.params;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Parses path looking for semicolon-separated parameters. Parameters are 
extracted and exposed as an
+ * immutable map. The path without parameters is available as raw path.
+ * 
+ * Parameters should be added immedietaly before or after selectors and 
extension:
+ * {@code /content/test;v='1.0'.html} or {@code /content/test.html;v=1.0}. 
Quotes can be used to escape the
+ * parameter value (it is necessary if the value contains dot and parameter is 
added before extension).
+ */
+public class ParsedParameters {
+
+    private final Map<String, String> parameters;
+
+    private final String parametersString;
+
+    private final String path;
+
+    /**
+     * Parse path and create parameters object.
+     * 
+     * @param fullPath Path to parse.
+     */
+    public ParsedParameters(final String fullPath) {
+        final PathParser parser = new PathParser();
+        parser.parse(fullPath);
+
+        parametersString = parser.getParametersString();
+        parameters = Collections.unmodifiableMap(parser.getParameters());
+        path = parser.getPath();
+    }
+
+    /**
+     * @return Path with no parameters.
+     */
+    public String getRawPath() {
+        return path;
+    }
+    
+    /**
+     * @return Path's substring containing parameters
+     */
+    public String getParametersString() {
+        return parametersString;
+    }
+
+    /**
+     * @return Map of the parameters.
+     */
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/PathParser.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/PathParser.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/PathParser.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/params/PathParser.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,171 @@
+/*
+ * 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.resourceresolver.impl.params;
+
+import java.util.Collections;
+import java.util.Map;
+
+class PathParser {
+
+    /**
+     * List of states. V1 and V2 prefixes means variant 1 and 2. In V1, the 
parameters are added after
+     * selectors and extension: {@code /content/test.sel.html;v=1.0}. In V2 
parameters are added before
+     * selectors and extension: {@code /content/test;v='1.0'.sel.html}
+     */
+    private enum ParserState {
+        INIT, V1_EXTENSION, V1_PARAMS, V2_PARAMS, V2_EXTENSION, SUFFIX, INVALID
+    }
+
+    private String rawPath;
+
+    private String parametersString;
+
+    private Map<String, String> parameters;
+
+    /**
+     * @return Path with no parameters.
+     */
+    public String getPath() {
+        return rawPath;
+    }
+
+    /**
+     * @return Path's substring containing parameters
+     */
+    public String getParametersString() {
+        return parametersString;
+    }
+
+    /**
+     * @return Parsed parameters.
+     */
+    public Map<String, String> getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Parses path containing parameters. Results will be available in {@link 
#rawPath} and {@link parameters}.
+     * 
+     * @param path
+     */
+    public void parse(String path) {
+        this.rawPath = path;
+        this.parameters = Collections.emptyMap();
+
+        if (path == null) {
+            return;
+        }
+
+        // indexOf shortcut for the most common case
+        final int di = path.indexOf('.');
+        final int si = path.indexOf(';');
+        if (di == -1 && si == -1) {
+            return;
+        }
+
+        final char[] chars = path.toCharArray();
+        final ParametersParser parametersParser = new ParametersParser();
+
+        ParserState state = ParserState.INIT;
+        int paramsStart = -1, paramsEnd = -1;
+
+        int i = (di != -1) ? ((si != -1) ? Math.min(di, si) : di) : si;
+        for (; i <= chars.length; i++) {
+            final char c;
+            if (i == chars.length) {
+                c = 0;
+            } else {
+                c = chars[i];
+            }
+
+            switch (state) {
+                case INIT:
+                    if (c == '.') {
+                        state = ParserState.V1_EXTENSION;
+                    } else if (c == ';') {
+                        paramsStart = i;
+                        i = parametersParser.parseParameters(chars, i, false);
+                        paramsEnd = i--;
+                        state = parametersParser.isInvalid() ? 
ParserState.INVALID : ParserState.V2_PARAMS;
+                    }
+                    break;
+
+                case V1_EXTENSION:
+                    if (c == '/') {
+                        state = ParserState.SUFFIX;
+                    } else if (c == ';') {
+                        paramsStart = i;
+                        i = parametersParser.parseParameters(chars, i, true);
+                        paramsEnd = i--;
+                        state = parametersParser.isInvalid() ? 
ParserState.INVALID : ParserState.V1_PARAMS;
+                    }
+                    break;
+
+                case V1_PARAMS:
+                    if (c == '/') {
+                        state = ParserState.SUFFIX;
+                    } else if (c == '.') {
+                        state = ParserState.INVALID; // no dots after params
+                    }
+                    break;
+
+                case V2_PARAMS:
+                    if (c == '/') {
+                        state = ParserState.INVALID; // there was no 
extension, so no suffix is allowed
+                    } else if (c == '.') {
+                        state = ParserState.V2_EXTENSION;
+                    }
+                    break;
+
+                case V2_EXTENSION:
+                    if (c == '/') {
+                        state = ParserState.SUFFIX;
+                    }
+                    break;
+
+                case SUFFIX:
+                case INVALID:
+                    break;
+            }
+        }
+
+        if (state == ParserState.INVALID) {
+            paramsStart = paramsEnd = -1;
+        } else {
+            cutPath(path, paramsStart, paramsEnd);
+            parameters = parametersParser.getParameters();
+        }
+    }
+
+    private void cutPath(String path, int from, int to) {
+        if (from == -1) {
+            rawPath = path;
+            parametersString = null;
+        } else if (to == -1) {
+            rawPath = path.substring(0, from);
+            parametersString = path.substring(from);
+        } else {
+            rawPath = path.substring(0, from) + path.substring(to);
+            parametersString = path.substring(from, to);
+        }
+    }
+
+
+}

Modified: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderHandler.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderHandler.java?rev=1709747&r1=1709746&r2=1709747&view=diff
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderHandler.java
 (original)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderHandler.java
 Wed Oct 21 08:23:09 2015
@@ -18,20 +18,32 @@
  */
 package org.apache.sling.resourceresolver.impl.providers;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.sling.api.SlingConstants;
+import 
org.apache.sling.resourceresolver.impl.legacy.LegacyResourceProviderWhiteboard;
+import org.apache.sling.resourceresolver.impl.providers.tree.Pathable;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
 
-public class ResourceProviderHandler implements 
Comparable<ResourceProviderHandler> {
+public class ResourceProviderHandler implements 
Comparable<ResourceProviderHandler>, Pathable {
 
     private final ResourceProviderInfo info;
 
     private final BundleContext bundleContext;
 
+    private final EventAdmin eventAdmin;
+
     private volatile ResourceProvider<?> provider;
 
-    public ResourceProviderHandler(final BundleContext bc, final 
ResourceProviderInfo info) {
+    public ResourceProviderHandler(final BundleContext bc, final 
ResourceProviderInfo info, final EventAdmin eventAdmin) {
         this.info = info;
         this.bundleContext = bc;
+        this.eventAdmin = eventAdmin;
     }
 
     public ResourceProviderInfo getInfo() {
@@ -46,20 +58,40 @@ public class ResourceProviderHandler imp
                     this.provider = (ResourceProvider<?>) 
this.bundleContext.getService(this.info.getServiceReference());
                 }
                 rp = this.provider;
+                postEvent(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED);
             }
         }
         return rp;
     }
 
+    private void postEvent(String topic) {
+        final Dictionary<String, Object> eventProps = new Hashtable<String, 
Object>();
+        eventProps.put(SlingConstants.PROPERTY_PATH, info.getPath());
+        String pid = (String) 
info.getServiceReference().getProperty(Constants.SERVICE_PID);
+        if (pid == null) {
+            pid = (String) 
info.getServiceReference().getProperty(LegacyResourceProviderWhiteboard.ORIGINAL_SERVICE_PID);
+        }
+        if (pid != null) {
+            eventProps.put(Constants.SERVICE_PID, pid);
+        }
+        eventAdmin.postEvent(new Event(topic, eventProps));
+    }
+
     public void deactivate() {
         if ( this.provider != null ) {
             this.provider = null;
             this.bundleContext.ungetService(this.info.getServiceReference());
+            postEvent(SlingConstants.TOPIC_RESOURCE_PROVIDER_REMOVED);
         }
     }
 
     @Override
     public int compareTo(final ResourceProviderHandler o) {
-        return this.info.compareTo(o.info);
+        return this.getInfo().compareTo(o.getInfo());
+    }
+
+    @Override
+    public String getPath() {
+        return this.getInfo().getPath();
     }
 }

Modified: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderInfo.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderInfo.java?rev=1709747&r1=1709746&r2=1709747&view=diff
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderInfo.java
 (original)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderInfo.java
 Wed Oct 21 08:23:09 2015
@@ -22,12 +22,16 @@ import org.apache.sling.api.resource.run
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Information about a registered resource provider
  */
 public class ResourceProviderInfo implements Comparable<ResourceProviderInfo> {
 
+    private static final Logger logger = 
LoggerFactory.getLogger(ResourceProviderInfo.class);
+
     private final ServiceReference ref;
 
     private final String path;
@@ -40,6 +44,12 @@ public class ResourceProviderInfo implem
 
     private final boolean modifiable;
 
+    private final boolean adaptable;
+
+    private final boolean refreshable;
+
+    private final boolean attributable;
+
     public ResourceProviderInfo(final ServiceReference ref) {
         this.ref = ref;
         this.path = 
PropertiesUtil.toString(ref.getProperty(ResourceProvider.PROPERTY_ROOT), "");
@@ -50,10 +60,13 @@ public class ResourceProviderInfo implem
         try {
             aType = AuthType.valueOf(authType);
         } catch ( final IllegalArgumentException iae) {
-            // ignore
+            logger.error("Illegal auth type {} for resource provider {}", 
authType, name);
         }
         this.authType = aType;
         this.modifiable = 
PropertiesUtil.toBoolean(ref.getProperty(ResourceProvider.PROPERTY_MODIFIABLE), 
false);
+        this.adaptable = 
PropertiesUtil.toBoolean(ref.getProperty(ResourceProvider.PROPERTY_ADAPTABLE), 
false);
+        this.refreshable = 
PropertiesUtil.toBoolean(ref.getProperty(ResourceProvider.PROPERTY_REFRESHABLE),
 false);
+        this.attributable = 
PropertiesUtil.toBoolean(ref.getProperty(ResourceProvider.PROPERTY_ATTRIBUTABLE),
 false);
     }
 
     public boolean isValid() {
@@ -98,6 +111,18 @@ public class ResourceProviderInfo implem
         return this.modifiable;
     }
 
+    public boolean isAdaptable() {
+        return adaptable;
+    }
+
+    public boolean isRefreshable() {
+        return refreshable;
+    }
+
+    public boolean isAttributable() {
+        return attributable;
+    }
+
     public String getName() {
         return this.name;
     }

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderStorage.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderStorage.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderStorage.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderStorage.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,108 @@
+/*
+ * 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.resourceresolver.impl.providers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.sling.api.resource.runtime.dto.AuthType;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathTree;
+
+public class ResourceProviderStorage {
+
+    private final List<ResourceProviderHandler> allHandlers;
+
+    private final List<ResourceProviderHandler> authRequiredHandlers;
+
+    private final List<ResourceProviderHandler> adaptableHandlers;
+
+    private final List<ResourceProviderHandler> attributableHandlers;
+
+    private final List<ResourceProviderHandler> refreshableHandlers;
+
+    private final List<ResourceProviderHandler> jcrQuerableHandlers;
+
+    private final List<ResourceProviderHandler> nativeQuerableHandlers;
+
+    private final PathTree<ResourceProviderHandler> handlersTree;
+
+    public ResourceProviderStorage(List<ResourceProviderHandler> handlers) {
+        this.allHandlers = handlers;
+        this.authRequiredHandlers = new ArrayList<ResourceProviderHandler>();
+        this.adaptableHandlers = new ArrayList<ResourceProviderHandler>();
+        this.attributableHandlers = new ArrayList<ResourceProviderHandler>();
+        this.refreshableHandlers = new ArrayList<ResourceProviderHandler>();
+        this.jcrQuerableHandlers = new ArrayList<ResourceProviderHandler>();
+        this.nativeQuerableHandlers = new ArrayList<ResourceProviderHandler>();
+        for (ResourceProviderHandler h : allHandlers) {
+            ResourceProviderInfo info = h.getInfo();
+            if (info.getAuthType() == AuthType.required) {
+                this.authRequiredHandlers.add(h);
+            }
+            if (info.isAdaptable()) {
+                this.adaptableHandlers.add(h);
+            }
+            if (info.isAttributable()) {
+                this.attributableHandlers.add(h);
+            }
+            if (info.isRefreshable()) {
+                this.refreshableHandlers.add(h);
+            }
+            if (h.getResourceProvider().getJCRQueryProvider() != null) {
+                this.jcrQuerableHandlers.add(h);
+            }
+            if (h.getResourceProvider().getQueryProvider() != null) {
+                this.nativeQuerableHandlers.add(h);
+            }
+        }
+        this.handlersTree = new PathTree<ResourceProviderHandler>(handlers);
+    }
+
+    public List<ResourceProviderHandler> getAllHandlers() {
+        return allHandlers;
+    }
+
+    public List<ResourceProviderHandler> getAuthRequiredHandlers() {
+        return authRequiredHandlers;
+    }
+
+    public List<ResourceProviderHandler> getAdaptableHandlers() {
+        return adaptableHandlers;
+    }
+
+    public List<ResourceProviderHandler> getAttributableHandlers() {
+        return attributableHandlers;
+    }
+
+    public List<ResourceProviderHandler> getRefreshableHandlers() {
+        return refreshableHandlers;
+    }
+
+    public List<ResourceProviderHandler> getJcrQuerableHandlers() {
+        return jcrQuerableHandlers;
+    }
+
+    public List<ResourceProviderHandler> getNativeQuerableHandlers() {
+        return nativeQuerableHandlers;
+    }
+
+    public PathTree<ResourceProviderHandler> getTree() {
+        return handlersTree;
+    }
+}

Modified: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java?rev=1709747&r1=1709746&r2=1709747&view=diff
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java
 (original)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java
 Wed Oct 21 08:23:09 2015
@@ -21,6 +21,7 @@ package org.apache.sling.resourceresolve
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -28,6 +29,7 @@ import java.util.concurrent.ConcurrentHa
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.resource.runtime.dto.FailureReason;
 import org.apache.sling.api.resource.runtime.dto.ResourceProviderDTO;
@@ -37,6 +39,7 @@ import org.apache.sling.spi.resource.pro
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
+import org.osgi.service.event.EventAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
@@ -58,6 +61,11 @@ public class ResourceProviderTracker {
 
     private final Map<ResourceProviderInfo, FailureReason> invalidProviders = 
new HashMap<ResourceProviderInfo, FailureReason>();
 
+    @Reference
+    private EventAdmin eventAdmin;
+
+    private volatile ResourceProviderStorage storage;
+
     @Activate
     protected void activate(final BundleContext bundleContext) {
         this.bundleContext = bundleContext;
@@ -88,6 +96,7 @@ public class ResourceProviderTracker {
                 return reference;
             }
         });
+        this.tracker.open();
     }
 
     @Deactivate
@@ -97,33 +106,38 @@ public class ResourceProviderTracker {
             this.tracker = null;
         }
         this.infos.clear();
+        this.handlers.clear();
+        this.invalidProviders.clear();
     }
 
     private void register(final ResourceProviderInfo info) {
         if ( info.isValid() ) {
            logger.debug("Registering new resource provider {}", info);
            synchronized ( this.handlers ) {
-               List<ResourceProviderHandler> infos = 
this.handlers.get(info.getPath());
-               if ( infos == null ) {
-                   infos = new ArrayList<ResourceProviderHandler>();
-                   this.handlers.put(info.getPath(), infos);
+               List<ResourceProviderHandler> matchingHandlers = 
this.handlers.get(info.getPath());
+               if ( matchingHandlers == null ) {
+                   matchingHandlers = new ArrayList<ResourceProviderHandler>();
+                   this.handlers.put(info.getPath(), matchingHandlers);
                }
-               final ResourceProviderHandler handler = new 
ResourceProviderHandler(bundleContext, info);
-               infos.add(handler);
-               Collections.sort(infos);
-               if ( infos.get(0) == handler ) {
+               final ResourceProviderHandler handler = new 
ResourceProviderHandler(bundleContext, info, eventAdmin);
+               matchingHandlers.add(handler);
+               Collections.sort(matchingHandlers);
+               if ( matchingHandlers.get(0) == handler ) {
                    if ( !this.activate(handler) ) {
-                       infos.remove(handler);
-                       if ( infos.isEmpty() ) {
+                       matchingHandlers.remove(handler);
+                       if ( matchingHandlers.isEmpty() ) {
                            this.handlers.remove(info.getPath());
                        }
                    } else {
-                       if ( infos.size() > 1 ) {
-                           this.deactivate(infos.get(1));
+                       if ( matchingHandlers.size() > 1 ) {
+                           this.deactivate(matchingHandlers.get(1));
                        }
                    }
                }
            }
+           synchronized(this) {
+               storage = null;
+           }
         } else {
             logger.debug("Ignoring invalid resource provider {}", info);
             synchronized ( this.invalidProviders ) {
@@ -135,30 +149,31 @@ public class ResourceProviderTracker {
     private void unregister(final ResourceProviderInfo info) {
         if ( info.isValid() ) {
             logger.debug("Unregistering resource provider {}", info);
-            final List<ResourceProviderHandler> infos = 
this.handlers.get(info.getPath());
-            if ( infos != null ) {
-                boolean activate = false;
-                if ( infos.get(0).getInfo() == info ) {
-                    activate = true;
-                    this.deactivate(infos.get(0));
-                }
-                if ( infos.remove(info) ) {
-                    if ( infos.isEmpty() ) {
-                        this.handlers.remove(info.getPath());
-                    } else {
-                        while ( activate ) {
-                            if ( !this.activate(infos.get(0)) ) {
-                                infos.remove(0);
-                                activate = !this.handlers.isEmpty();
-                                if ( !activate ) {
-                                    this.handlers.remove(info.getPath());
-                                }
+            synchronized (this.handlers) {
+                final List<ResourceProviderHandler> matchingHandlers = 
this.handlers.get(info.getPath());
+                if ( matchingHandlers != null ) {
+                    boolean doActivateNext = false;
+                    if ( matchingHandlers.get(0).getInfo() == info ) {
+                        doActivateNext = true;
+                        this.deactivate(matchingHandlers.get(0));
+                    }
+                    if (removeHandlerByInfo(info, matchingHandlers)) {
+                        while (doActivateNext && !matchingHandlers.isEmpty()) {
+                            if (this.activate(matchingHandlers.get(0))) {
+                                doActivateNext = false;
+                            } else {
+                                matchingHandlers.remove(0);
                             }
                         }
                     }
+                    if (matchingHandlers.isEmpty()) {
+                        this.handlers.remove(info.getPath());
+                    }
                 }
             }
-
+            synchronized(this) {
+                storage = null;
+            }
         } else {
             logger.debug("Unregistering invalid resource provider {}", info);
             synchronized ( this.invalidProviders ) {
@@ -167,6 +182,19 @@ public class ResourceProviderTracker {
         }
     }
 
+    private boolean removeHandlerByInfo(final ResourceProviderInfo info, final 
List<ResourceProviderHandler> infos) {
+        Iterator<ResourceProviderHandler> it = infos.iterator();
+        boolean removed = false;
+        while (it.hasNext()) {
+            if (it.next().getInfo() == info) {
+                it.remove();
+                removed = true;
+                break;
+            }
+        }
+        return removed;
+    }
+
     private void deactivate(final ResourceProviderHandler handler) {
         handler.deactivate();
         logger.debug("Deactivated resource provider {}", handler.getInfo());
@@ -217,6 +245,32 @@ public class ResourceProviderTracker {
         dto.failedProviders = failures.toArray(new 
ResourceProviderFailureDTO[failures.size()]);
     }
 
+    private List<ResourceProviderHandler> getHandlers() {
+        List<ResourceProviderHandler> result = new 
ArrayList<ResourceProviderHandler>();
+        synchronized (this.handlers) {
+            for (List<ResourceProviderHandler> list : handlers.values()) {
+                ResourceProviderHandler h  = list.get(0);
+                if (h != null) {
+                    result.add(h);
+                }
+            }
+        }
+        return result;
+    }
+
+    public ResourceProviderStorage getResourceProviderStorage() {
+        ResourceProviderStorage result = storage;
+        if (result == null) {
+            synchronized(this) {
+                if (storage == null) {
+                    storage = new ResourceProviderStorage(getHandlers());
+                }
+                result = storage;
+            }
+        }
+        return result;
+    }
+
     private void fill(final ResourceProviderDTO d, final ResourceProviderInfo 
info) {
         d.authType = info.getAuthType();
         d.modifiable = info.getModifiable();

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AbstractIterator.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AbstractIterator.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AbstractIterator.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AbstractIterator.java
 Wed Oct 21 08:23:09 2015
@@ -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.resourceresolver.impl.providers.stateful;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public abstract class AbstractIterator<T> implements Iterator<T> {
+
+    private T nextElement;
+
+    protected abstract T seek();
+
+    @Override
+    public boolean hasNext() {
+        if (nextElement == null) {
+            nextElement = seek();
+        }
+        return nextElement != null;
+    }
+
+    @Override
+    public T next() {
+        if (nextElement == null && !hasNext()) {
+            throw new NoSuchElementException();
+        }
+        final T result = nextElement;
+        nextElement = null;
+        return result;
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,384 @@
+/*
+ * 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.resourceresolver.impl.providers.stateful;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+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.query.Query;
+import org.apache.sling.api.resource.query.QueryInstructions;
+import org.apache.sling.api.resource.runtime.dto.AuthType;
+import org.apache.sling.resourceresolver.impl.BasicResolveContext;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
+import org.apache.sling.spi.resource.provider.JCRQueryProvider;
+import org.apache.sling.spi.resource.provider.QueryProvider;
+import org.apache.sling.spi.resource.provider.QueryResult;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This {@link StatefulResourceProvider} implementation authenticates the
+ * underlying {@link ResourceProvider}. The authentication can be done during
+ * creation of the object (for {@link AuthType#required}) or before invoking 
the
+ * first method (for {@link AuthType#lazy}).
+ */
+public class AuthenticatedResourceProvider implements StatefulResourceProvider 
{
+
+    private static final Logger logger = 
LoggerFactory.getLogger(AuthenticatedResourceProvider.class);
+
+    private static final String FORBIDDEN_ATTRIBUTE = 
ResourceResolverFactory.PASSWORD;
+
+    private final ResourceProvider<Object> rp;
+
+    private final ResourceProviderInfo info;
+
+    private final Map<String, Object> authInfo;
+
+    private final ResourceResolver resolver;
+
+    private boolean authenticated;
+
+    private Object contextData;
+
+    private ResolveContext<Object> cachedContext;
+
+    private QueryProvider<Object> cachedQueryProvider;
+
+    private JCRQueryProvider<Object> cachedJcrQueryProvider;
+
+    @SuppressWarnings("unchecked")
+    public AuthenticatedResourceProvider(ResourceProvider<?> rp, 
ResourceProviderInfo info, ResourceResolver resolver,
+            Map<String, Object> authInfo) throws LoginException {
+        this.rp = (ResourceProvider<Object>) rp;
+        this.info = info;
+        this.authInfo = authInfo;
+        this.resolver = resolver;
+        if (info.getAuthType() == AuthType.required) {
+            authenticate();
+        }
+    }
+
+    private Object authenticate() throws LoginException {
+        if (!authenticated && (info.getAuthType() == AuthType.required || 
info.getAuthType() == AuthType.lazy)) {
+            contextData = rp.authenticate(authInfo);
+            authenticated = true;
+        }
+        return contextData;
+    }
+
+    private ResolveContext<Object> getBasicContext() throws LoginException {
+        if (cachedContext != null) {
+            return cachedContext;
+        }
+        return cachedContext = getContext(null, null);
+    }
+
+    @Override
+    public ResolveContext<Object> getContext(Map<String, String> parameters) 
throws LoginException {
+        if (parameters == null || parameters.isEmpty()) {
+            return getBasicContext();
+        } else {
+            return getContext(parameters, null);
+        }
+    }
+
+    private ResolveContext<Object> getContext(Map<String, String> parameters, 
List<StatefulResourceProvider> parentProviders) throws LoginException {
+        ResourceProvider<Object> parentProvider = null;
+        ResolveContext<Object> parentContext = null;
+        try {
+            if (parentProviders != null && !parentProviders.isEmpty()) {
+                StatefulResourceProvider statefulParentProvider = 
parentProviders.get(0);
+                parentProvider = statefulParentProvider.getResourceProvider();
+                parentContext = statefulParentProvider.getContext(parameters);
+            }
+        } catch (LoginException e) {
+            logger.warn("Can't authenticate the parent resource provider", e);
+        }
+        return new BasicResolveContext<Object>(resolver, parameters, 
authenticate(), parentProvider, parentContext);
+    }
+
+    @Override
+    public void logout() {
+        if (authenticated) {
+            try {
+                rp.logout(getBasicContext().getProviderState());
+            } catch (LoginException e) {
+                logger.error("Can't create context", e);
+            }
+            authenticated = false;
+            cachedContext = null;
+        }
+    }
+
+    @Override
+    public void refresh() {
+        try {
+            rp.refresh(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+    }
+
+    @Override
+    public boolean isLive() {
+        try {
+            return rp.isLive(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return false;
+        }
+    }
+
+    @Override
+    public Resource getParent(Resource child, List<StatefulResourceProvider> 
parentProviders) {
+        try {
+            return 
rp.getParent(getContext(child.getResourceMetadata().getParameterMap(), 
parentProviders), child);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public Resource getResource(String path, Resource parent, Map<String, 
String> parameters, boolean isResolve, List<StatefulResourceProvider> 
parentProviders) {
+        try {
+            return rp.getResource(getContext(parameters, parentProviders), 
path, parent);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+
+    }
+
+    @Override
+    public Iterator<Resource> listChildren(Resource parent, 
List<StatefulResourceProvider> parentProviders) {
+        try {
+            return 
rp.listChildren(getContext(parent.getResourceMetadata().getParameterMap(), 
parentProviders), parent);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public Collection<String> getAttributeNames() {
+        Set<String> attributeNames = new LinkedHashSet<String>();
+        Collection<String> rpAttributeNames = null;
+        try {
+            rpAttributeNames = rp.getAttributeNames(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+        if (rpAttributeNames != null) {
+            attributeNames.addAll(rpAttributeNames);
+        }
+        if (authInfo != null) {
+            attributeNames.addAll(authInfo.keySet());
+        }
+        attributeNames.remove(FORBIDDEN_ATTRIBUTE);
+        return attributeNames;
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+        if (FORBIDDEN_ATTRIBUTE.equals(name)) {
+            return null;
+        }
+        Object attribute = null;
+        try {
+            attribute = rp.getAttribute(getBasicContext(), name);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+        if (attribute == null) {
+            attribute = authInfo.get(name);
+        }
+        return attribute;
+    }
+
+    @Override
+    public Resource create(String path, Map<String, Object> properties, 
List<StatefulResourceProvider> parentProviders) throws PersistenceException {
+        try {
+            return rp.create(getContext(null, parentProviders), path, 
properties);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public void delete(Resource resource, List<StatefulResourceProvider> 
parentProviders) throws PersistenceException {
+        try {
+            
rp.delete(getContext(resource.getResourceMetadata().getParameterMap(), 
parentProviders), resource);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+    }
+
+    @Override
+    public void revert() {
+        try {
+            rp.revert(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+    }
+
+    @Override
+    public void commit() throws PersistenceException {
+        try {
+            rp.commit(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+        }
+    }
+
+    @Override
+    public boolean hasChanges() {
+        try {
+            return rp.hasChanges(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return false;
+        }
+    }
+
+    private QueryProvider<Object> getQueryProvider() {
+        if (cachedQueryProvider == null) {
+            cachedQueryProvider = rp.getQueryProvider();
+        }
+        return cachedQueryProvider;
+    }
+
+    private JCRQueryProvider<Object> getJcrQueryProvider() {
+        if (cachedJcrQueryProvider == null) {
+            cachedJcrQueryProvider = rp.getJCRQueryProvider();
+        }
+        return cachedJcrQueryProvider;
+    }
+
+    @Override
+    public QueryResult find(Query q, QueryInstructions qi) {
+        final QueryProvider<Object> provider = getQueryProvider();
+        if (provider == null) {
+            return null;
+        }
+        try {
+            return provider.find(getBasicContext(), q, qi);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public String[] getSupportedLanguages() {
+        final JCRQueryProvider<Object> jcrQueryProvider = 
getJcrQueryProvider();
+        if (jcrQueryProvider == null) {
+            return null;
+        }
+        try {
+            return jcrQueryProvider.getSupportedLanguages(getBasicContext());
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+    }
+
+    @Override
+    public Iterator<Resource> findResources(String query, String language) {
+        final JCRQueryProvider<Object> jcrQueryProvider = 
getJcrQueryProvider();
+        if (jcrQueryProvider == null) {
+            return null;
+        }
+        try {
+            return jcrQueryProvider.findResources(getBasicContext(), query, 
language);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public Iterator<Map<String, Object>> queryResources(String query, String 
language) {
+        final JCRQueryProvider<Object> jcrQueryProvider = 
getJcrQueryProvider();
+        if (jcrQueryProvider == null) {
+            return null;
+        }
+        try {
+            return (Iterator) 
jcrQueryProvider.queryResources(getBasicContext(), query, language);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        try {
+            return rp.adaptTo(getBasicContext(), type);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean copy(String srcAbsPath, String destAbsPath, 
List<StatefulResourceProvider> parentProviders) throws PersistenceException {
+        try {
+            return rp.copy(getContext(null, parentProviders), srcAbsPath, 
destAbsPath);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean move(String srcAbsPath, String destAbsPath, 
List<StatefulResourceProvider> parentProviders) throws PersistenceException {
+        try {
+            return rp.move(getContext(null, parentProviders), srcAbsPath, 
destAbsPath);
+        } catch (LoginException e) {
+            logger.error("Can't create context", e);
+            return false;
+        }
+    }
+
+    @Override
+    public ResourceResolver getResourceResolver() {
+        return resolver;
+    }
+
+    @Override
+    public ResourceProvider<Object> getResourceProvider() {
+        return rp;
+    }
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/CombinedResourceProvider.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/CombinedResourceProvider.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/CombinedResourceProvider.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/CombinedResourceProvider.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,587 @@
+/*
+ * 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.resourceresolver.impl.providers.stateful;
+
+import static org.apache.commons.collections.IteratorUtils.chainedIterator;
+import static org.apache.commons.collections.IteratorUtils.transformedIterator;
+import static org.apache.sling.api.resource.ResourceUtil.getName;
+import static 
org.apache.sling.spi.resource.provider.ResourceProvider.RESOURCE_TYPE_SYNTHETIC;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.ListUtils;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.query.Query;
+import org.apache.sling.api.resource.query.QueryInstructions;
+import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
+import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
+import org.apache.sling.resourceresolver.impl.providers.tree.Node;
+import org.apache.sling.spi.resource.provider.QueryResult;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class takes a number of {@link StatefulResourceProvider} objects and
+ * exposes it as one such object. Provider appropriate for the given operation
+ * is chosen basing on its {@link ResourceProviderInfo#getPath()} (more 
specific
+ * first) and service ranking.
+ */
+public class CombinedResourceProvider {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(CombinedResourceProvider.class);
+
+    private static final StatefulResourceProvider EMPTY_PROVIDER = new 
EmptyResourceProvider();
+
+    private final ResourceProviderStorage storage;
+
+    private final ResourceResolver resolver;
+
+    private final ResourceProviderAuthenticator authenticator;
+
+    public CombinedResourceProvider(ResourceProviderStorage storage, 
ResourceResolver resolver, ResourceProviderAuthenticator authenticator) {
+        this.storage = storage;
+        this.resolver = resolver;
+        this.authenticator = authenticator;
+    }
+
+    /**
+     * Logouts from all providers.
+     */
+    public void logout() {
+        for (StatefulResourceProvider p : 
authenticator.getAllUsedAuthenticated()) {
+            p.logout();
+        }
+    }
+
+    /**
+     * Refreshes all providers.
+     */
+    public void refresh() {
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getRefreshableHandlers())) {
+            p.refresh();
+        }
+    }
+
+    /**
+     * Returns {@code true} if all providers are live.
+     */
+    public boolean isLive() {
+        for (StatefulResourceProvider p : 
authenticator.getAllUsedAuthenticated()) {
+            if (!p.isLive()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns parent from the most appropriate resource provider accepting the
+     * given children.
+     * 
+     * In some cases the {@link SyntheticResource} can be returned if no
+     * resource provider returns parent for this child. See
+     * {@link #getResource(String, Resource, Map, boolean)} for more details
+     */
+    public Resource getParent(Resource child) {
+        String path = child.getPath();
+        List<StatefulResourceProvider> matching = getMatchingProviders(path);
+        Resource parentCandidate = head(matching).getParent(child, 
tail(matching));
+        if (parentCandidate != null) {
+            return parentCandidate;
+        }
+        String parentPath = ResourceUtil.getParent(path);
+        if (parentPath != null && isIntermediatePath(parentPath)) {
+            return new SyntheticResource(resolver, parentPath, 
ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+        }
+        return null;
+    }
+
+    /**
+     * Returns resource from the most appropriate resource provider.
+     * <br/><br/>
+     * If there's no such provider and the path is a part of some resource
+     * provider path, then the {@link SyntheticResource} will be returned. For
+     * instance, if we have resource provider under
+     * {@code /libs/sling/servlet/default/GET.servlet} and no resource provider
+     * returns a resource for {@code /libs/sling/servlet/default}, then the
+     * {@link SyntheticResource} will be returned to provide a consistent
+     * resource tree.
+     * <br/><br/>
+     * The same behaviour occurs in {@link #getParent(Resource)} and
+     * {@link #listChildren(Resource)}.
+     */
+    public Resource getResource(String path, Resource parent, Map<String, 
String> parameters, boolean isResolve) {
+        if (path == null || path.length() == 0 || path.charAt(0) != '/') {
+            logger.debug("Not absolute {}", path);
+            return null; // path must be absolute
+        }
+
+        try {
+            List<StatefulResourceProvider> matching = 
getMatchingProviders(path);
+            Resource resourceCandidate = head(matching).getResource(path, 
parent, parameters, isResolve, tail(matching));
+            if (resourceCandidate != null) {
+                return resourceCandidate;
+            }
+
+            // query: /libs/sling/servlet/default
+            // resource Provider: libs/sling/servlet/default/GET.servlet
+            // list will match libs, sling, servlet, default
+            // and there will be no resource provider at the end
+            // SLING-3482 : this is only done for getResource but not resolve
+            //              as it is important e.g. for servlet resolution
+            //              to get the parent resource for resource traversal.
+            if (!isResolve && isIntermediatePath(path)) {
+                logger.debug("Resolved Synthetic {}", path);
+                return new SyntheticResource(resolver, path, 
ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+            }
+        } catch (SlingException e) {
+            throw e;
+        } catch (Exception e) {
+            logger.warn("Unexpected exception while trying to get resource for 
" + path, e);
+        }
+        logger.debug("Resource null {} ", path);
+        return null;
+    }
+
+    private boolean isIntermediatePath(final String fullPath) {
+        return storage.getTree().getNode(fullPath) != null;
+    }
+
+    /**
+     * This method asks all matching resource providers for the children 
iterators,
+     * merges them, adds {@link SyntheticResource}s (see
+     * {@link #getResource(String, Resource, Map, boolean)} for more details),
+     * filters out the duplicates and returns the resulting iterator. All
+     * transformations are done lazily, during the {@link Iterator#hasNext()}
+     * invocation on the result.
+     */
+    @SuppressWarnings("unchecked")
+    public Iterator<Resource> listChildren(final Resource parent) {
+        List<StatefulResourceProvider> matching = 
getMatchingProviders(parent.getPath());
+        Iterator<Resource> realChildren = head(matching).listChildren(parent, 
tail(matching));
+        Iterator<Resource> syntheticChildren = 
getSyntheticChildren(parent).iterator();
+        Iterator<Resource> allChildren;
+        if (realChildren == null) {
+            allChildren = syntheticChildren;
+        } else {
+            allChildren = new UniqueIterator(chainedIterator(realChildren, 
syntheticChildren));
+        } 
+        return transformedIterator(allChildren, new Transformer() {
+            @Override
+            public Object transform(Object input) {
+                Resource resource = (Resource) input;
+                
resource.getResourceMetadata().setResolutionPath(resource.getPath());
+                return resource;
+            }
+        });
+    }
+
+    private List<Resource> getSyntheticChildren(Resource parent) {
+        Node<ResourceProviderHandler> node = 
storage.getTree().getNode(parent.getPath());
+        if (node == null) {
+            return Collections.emptyList();
+        }
+        List<Resource> children = new ArrayList<Resource>();
+        for (Entry<String, Node<ResourceProviderHandler>> entry : 
node.getChildren().entrySet()) {
+            final String name = entry.getKey();
+            final ResourceProviderHandler handler = 
entry.getValue().getValue();
+            final String childPath = new 
StringBuilder(parent.getPath()).append('/').append(name).toString();
+            final Resource child;
+            if (handler == null) {
+                child = new SyntheticResource(resolver, childPath, 
RESOURCE_TYPE_SYNTHETIC);
+            } else { 
+                child = 
authenticator.getStateful(handler).getResource(childPath, parent, null, false, 
null);
+            }
+            if (child != null) {
+                children.add(child);
+            }
+        }
+        return children;
+    }
+
+    /**
+     * Returns the union of all attribute names.
+     */
+    public Collection<String> getAttributeNames() {
+        final Set<String> names = new LinkedHashSet<String>();
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getAttributableHandlers())) {
+            Collection<String> newNames = p.getAttributeNames();
+            if (newNames != null) {
+                names.addAll(newNames);
+            }
+        }
+        return names;
+    }
+
+    /**
+     * Returns the first non-null result of the
+     * {@link StatefulResourceProvider#getAttribute(String)} invocation on
+     * the providers.
+     */
+    public Object getAttribute(String name) {
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getAttributableHandlers())) {
+            Object attribute = p.getAttribute(name);
+            if (attribute != null) {
+                return attribute;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Create a resource. Iterate over all modifiable ResourceProviders
+     * stopping at the first one which creates the resource and return the
+     * created resource.
+     *
+     * @throws UnsupportedOperationException
+     *             If creation is not allowed/possible
+     * @throws PersistenceException
+     *             If creation fails
+     * @return The new resource
+     */
+    public Resource create(String path, Map<String, Object> properties) throws 
PersistenceException {
+        List<StatefulResourceProvider> matching = 
getMatchingModifiableProviders(path);
+        Resource creationResultResource = head(matching).create(path, 
properties, tail(matching));
+        if (creationResultResource != null) {
+            return creationResultResource;
+        }
+        // If none of the viable handlers could create the resource or if the
+        // list of handlers was empty, throw an Exception
+        throw new UnsupportedOperationException("create '" + getName(path) + 
"' at " + ResourceUtil.getParent(path));
+    }
+
+    /**
+     * Delete the resource. Iterate over all modifiable ResourceProviders
+     * giving each an opportunity to delete the resource if they are able.
+     *
+     * @throws NullPointerException
+     *             if resource is null
+     * @throws UnsupportedOperationException
+     *             If deletion is not allowed/possible
+     * @throws PersistenceException
+     *             If deletion fails
+     */
+    public void delete(Resource resource) throws PersistenceException {
+        final String path = resource.getPath();
+        final Map<String, String> parameters = 
resource.getResourceMetadata().getParameterMap();
+        boolean anyProviderAttempted = false;
+
+        // Give all viable handlers a chance to delete the resource
+        for (StatefulResourceProvider p : 
getMatchingModifiableProviders(path)) {
+            Resource providerResource = p.getResource(path, null, parameters, 
false, null);
+            if (providerResource != null) {
+                anyProviderAttempted = true;
+                p.delete(providerResource, null);
+            }
+        }
+        // If none of the viable handlers could delete the resource or if the
+        // list of handlers was empty, throw an Exception
+        if (!anyProviderAttempted) {
+            throw new UnsupportedOperationException("delete at '" + path + 
"'");
+        }
+    }
+
+    /**
+     * Revert changes on all modifiable ResourceProviders.
+     */
+    public void revert() {
+        for (StatefulResourceProvider p : 
authenticator.getAllUsedModifiable()) {
+            p.revert();
+        }
+    }
+
+    /**
+     * Commit changes on all modifiable ResourceProviders.
+     */
+    public void commit() throws PersistenceException {
+        for (StatefulResourceProvider p : 
authenticator.getAllUsedModifiable()) {
+            p.commit();
+        }
+    }
+
+    /**
+     * Check if any modifiable ResourceProvider has uncommited changes.
+     */
+    public boolean hasChanges() {
+        for (StatefulResourceProvider p : 
authenticator.getAllUsedModifiable()) {
+            if (p.hasChanges()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Queries all resource providers and combines the results.
+     */
+    public QueryResult find(final Query q, final QueryInstructions qi) {
+        return new CombinedQueryResult(q, qi);
+    }
+
+    /**
+     * Return the union of query languages supported by the providers.
+     */
+    public String[] getSupportedLanguages() {
+        Set<String> supportedLanguages = new LinkedHashSet<String>();
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getJcrQuerableHandlers())) {
+            
supportedLanguages.addAll(Arrays.asList(p.getSupportedLanguages()));
+        }
+        return supportedLanguages.toArray(new 
String[supportedLanguages.size()]);
+    }
+
+    /**
+     * Queries all resource providers and combines the results.
+     */
+    public Iterator<Resource> findResources(final String query, final String 
language) {
+        List<StatefulResourceProvider> querableRP = 
getQuerableProviders(language);
+        List<Iterator<Resource>> iterators = new 
ArrayList<Iterator<Resource>>(querableRP.size());
+        for (StatefulResourceProvider p : querableRP) {
+            iterators.add(p.findResources(query, language));
+        }
+        return new ChainedIterator<Resource>(iterators.iterator());
+    }
+
+    private List<StatefulResourceProvider> getQuerableProviders(String 
language) {
+        List<StatefulResourceProvider> querableProviders = new 
ArrayList<StatefulResourceProvider>();
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getJcrQuerableHandlers())) {
+            if (ArrayUtils.contains(p.getSupportedLanguages(), language)) {
+                querableProviders.add(p);
+            }
+        }
+        return querableProviders;
+    }
+
+    /**
+     * Queries all resource providers and combines the results.
+     */
+    public Iterator<Map<String, Object>> queryResources(final String query, 
final String language) {
+        List<StatefulResourceProvider> querableRP = 
getQuerableProviders(language);
+        List<Iterator<Map<String, Object>>> iterators = new 
ArrayList<Iterator<Map<String, Object>>>(querableRP.size());
+        for (StatefulResourceProvider p : querableRP) {
+            iterators.add(p.queryResources(query, language));
+        }
+        return new ChainedIterator<Map<String, Object>>(iterators.iterator());
+    }
+
+    /**
+     * Returns the first non-null result of the adaptTo() method invoked on the
+     * providers.
+     */
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        for (StatefulResourceProvider p : 
authenticator.getAll(storage.getAdaptableHandlers())) {
+            final Object adaptee = p.adaptTo(type);
+            if (adaptee != null) {
+                return (AdapterType) adaptee;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Tries to find a resource provider accepting both paths and invokes
+     * {@link StatefulResourceProvider#copy(String, String)} method on it.
+     * Returns false if there's no such provider.
+     */
+    public boolean copy(String srcAbsPath, String destAbsPath) throws 
PersistenceException {
+        List<StatefulResourceProvider> srcProviders = 
getMatchingProviders(srcAbsPath);
+        List<StatefulResourceProvider> dstProviders = 
getMatchingModifiableProviders(destAbsPath);
+        @SuppressWarnings("unchecked")
+        List<StatefulResourceProvider> intersection = 
ListUtils.intersection(srcProviders, dstProviders);
+        return head(intersection).copy(srcAbsPath, destAbsPath, 
tail(intersection));
+    }
+
+    /**
+     * Tries to find a resource provider accepting both paths and invokes
+     * {@link StatefulResourceProvider#move(String, String)} method on it.
+     * Returns false if there's no such provider.
+     */
+    public boolean move(String srcAbsPath, String destAbsPath) throws 
PersistenceException {
+        List<StatefulResourceProvider> srcProviders = 
getMatchingModifiableProviders(srcAbsPath);
+        List<StatefulResourceProvider> dstProviders = 
getMatchingModifiableProviders(destAbsPath);
+        @SuppressWarnings("unchecked")
+        List<StatefulResourceProvider> intersection = 
ListUtils.intersection(srcProviders, dstProviders);
+        return head(intersection).move(srcAbsPath, destAbsPath, 
tail(intersection));
+    }
+
+    private List<StatefulResourceProvider> getMatchingProviders(String path) {
+        List<ResourceProviderHandler> handlers = 
storage.getTree().getMatchingNodes(path);
+        StatefulResourceProvider[] matching = new 
StatefulResourceProvider[handlers.size()];
+        int i = matching.length - 1;
+        for (ResourceProviderHandler h : handlers) {
+            matching[i--] = authenticator.getStateful(h); // reverse order
+        }
+        return Arrays.asList(matching);
+    }
+
+    private List<StatefulResourceProvider> 
getMatchingModifiableProviders(String path) {
+        List<ResourceProviderHandler> handlers = 
storage.getTree().getMatchingNodes(path);
+        List<StatefulResourceProvider> matching = new 
ArrayList<StatefulResourceProvider>(handlers.size());
+        for (ResourceProviderHandler h : handlers) {
+            if (h.getInfo().getModifiable()) {
+                matching.add(authenticator.getStateful(h));
+            }
+        }
+        Collections.reverse(matching);
+        return matching;
+    }
+
+    private static StatefulResourceProvider 
head(List<StatefulResourceProvider> list) {
+        if (list.isEmpty()) {
+            return EMPTY_PROVIDER;
+        } else {
+            return list.get(0);
+        }
+    }
+
+    private static <T> List<T> tail(List<T> list) {
+        if (list.isEmpty()) {
+            return Collections.emptyList();
+        } else {
+            return list.subList(1, list.size());
+        }
+    }
+
+    private class CombinedQueryResult extends QueryResult implements 
Iterable<Resource> {
+
+        private final Query q;
+
+        private final QueryInstructions qi;
+
+        public CombinedQueryResult(Query q, QueryInstructions qi) {
+            this.q = q;
+            this.qi = qi;
+        }
+
+        @Override
+        public Iterable<Resource> getResources() {
+            return this;
+        }
+
+        @Override
+        public Iterator<Resource> iterator() {
+            @SuppressWarnings("unchecked")
+            Iterator<Iterator<Resource>> iterators = 
IteratorUtils.transformedIterator(authenticator.getAll(storage.getNativeQuerableHandlers()).iterator(),
+                    new Transformer() {
+                        @Override
+                        public Object transform(Object input) {
+                            StatefulResourceProvider rp = 
(StatefulResourceProvider) input;
+                            return rp.find(q, qi).getResources().iterator();
+                        }
+                    });
+            return new ChainedIterator<Resource>(iterators);
+        }
+    }
+
+    private static class ChainedIterator<T> extends AbstractIterator<T> {
+
+        private final Iterator<Iterator<T>> iterators;
+
+        private Iterator<T> currentIterator;
+
+        public ChainedIterator(Iterator<Iterator<T>> iterators) {
+            this.iterators = iterators;
+        }
+
+        @Override
+        protected T seek() {
+            while (true) {
+                if (currentIterator == null) {
+                    if (!iterators.hasNext()) {
+                        return null;
+                    }
+                    currentIterator = iterators.next();
+                    continue;
+                }
+                if (currentIterator.hasNext()) {
+                    return currentIterator.next();
+                } else {
+                    currentIterator = null;
+                }
+            }
+        }
+    }
+    
+    /**
+     * This iterator removes duplicated Resource entries. Regular resources
+     * overrides the synthetic ones.
+     */
+    private static class UniqueIterator extends AbstractIterator<Resource> {
+
+        private final Iterator<Resource> input;
+
+        private final List<String> visited;
+
+        private final Map<String, Resource> delayed;
+
+        private Iterator<Resource> delayedIterator;
+
+        public UniqueIterator(Iterator<Resource> input) {
+            this.input = input;
+            this.visited = new ArrayList<String>();
+            this.delayed = new LinkedHashMap<String, Resource>();
+        }
+
+        @Override
+        protected Resource seek() {
+            while (input.hasNext()) {
+                Resource next = input.next();
+                String path = next.getPath();
+
+                if (visited.contains(path)) {
+                    continue;
+                } else if (next instanceof SyntheticResource) {
+                    delayed.put(path, next);
+                } else {
+                    visited.add(path);
+                    delayed.remove(path);
+                    return next;
+                }
+            }
+
+            if (delayedIterator == null) {
+                delayedIterator = delayed.values().iterator();
+            }
+            if (delayedIterator.hasNext()) {
+                return delayedIterator.next();
+            }
+            return null;
+        }
+    }
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/EmptyResourceProvider.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/EmptyResourceProvider.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/EmptyResourceProvider.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/EmptyResourceProvider.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,151 @@
+/*
+ * 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.resourceresolver.impl.providers.stateful;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.query.Query;
+import org.apache.sling.api.resource.query.QueryInstructions;
+import org.apache.sling.spi.resource.provider.QueryResult;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+
+public class EmptyResourceProvider implements StatefulResourceProvider {
+
+    @Override
+    public ResourceResolver getResourceResolver() {
+        return null;
+    }
+
+    @Override
+    public void logout() {
+    }
+
+    @Override
+    public void refresh() {
+    }
+
+    @Override
+    public boolean isLive() {
+        return false;
+    }
+
+    @Override
+    public Resource getParent(Resource child, List<StatefulResourceProvider> 
parentProviders) {
+        return null;
+    }
+
+    @Override
+    public Resource getResource(String path, Resource parent, Map<String, 
String> parameters, boolean isResolve,
+            List<StatefulResourceProvider> parentProviders) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Resource> listChildren(Resource parent, 
List<StatefulResourceProvider> parentProviders) {
+        return null;
+    }
+
+    @Override
+    public Collection<String> getAttributeNames() {
+        return null;
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+        return null;
+    }
+
+    @Override
+    public Resource create(String path, Map<String, Object> properties, 
List<StatefulResourceProvider> parentProviders)
+            throws PersistenceException {
+        return null;
+    }
+
+    @Override
+    public void delete(Resource resource, List<StatefulResourceProvider> 
parentProviders) throws PersistenceException {
+    }
+
+    @Override
+    public void revert() {
+    }
+
+    @Override
+    public void commit() throws PersistenceException {
+    }
+
+    @Override
+    public boolean hasChanges() {
+        return false;
+    }
+
+    @Override
+    public QueryResult find(Query q, QueryInstructions qi) {
+        return null;
+    }
+
+    @Override
+    public String[] getSupportedLanguages() {
+        return null;
+    }
+
+    @Override
+    public Iterator<Resource> findResources(String query, String language) {
+        return null;
+    }
+
+    @Override
+    public Iterator<Map<String, Object>> queryResources(String query, String 
language) {
+        return null;
+    }
+
+    @Override
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return null;
+    }
+
+    @Override
+    public boolean copy(String srcAbsPath, String destAbsPath, 
List<StatefulResourceProvider> parentProviders)
+            throws PersistenceException {
+        return false;
+    }
+
+    @Override
+    public boolean move(String srcAbsPath, String destAbsPath, 
List<StatefulResourceProvider> parentProviders)
+            throws PersistenceException {
+        return false;
+    }
+
+    @Override
+    public ResourceProvider<Object> getResourceProvider() {
+        return null;
+    }
+
+    @Override
+    public ResolveContext<Object> getContext(Map<String, String> parameters) {
+        return null;
+    }
+
+}

Added: 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java?rev=1709747&view=auto
==============================================================================
--- 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java
 (added)
+++ 
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java
 Wed Oct 21 08:23:09 2015
@@ -0,0 +1,127 @@
+/*
+ * 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.resourceresolver.impl.providers.stateful;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.runtime.dto.AuthType;
+import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
+import 
org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ResourceProviderAuthenticator {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ResourceProviderAuthenticator.class);
+
+    private final Map<ResourceProviderHandler, StatefulResourceProvider> 
stateful;
+
+    private final List<StatefulResourceProvider> authenticated;
+
+    private final List<StatefulResourceProvider> authenticatedModifiable;
+
+    private final ResourceResolver resolver;
+
+    private final Map<String, Object> authInfo;
+
+    private final ResourceAccessSecurityTracker securityTracker;
+
+    boolean allProvidersAuthenticated;
+
+    public ResourceProviderAuthenticator(ResourceResolver resolver, 
Map<String, Object> authInfo,
+            ResourceAccessSecurityTracker securityTracker) throws 
LoginException {
+        this.stateful = new IdentityHashMap<ResourceProviderHandler, 
StatefulResourceProvider>();
+        this.authenticated = new ArrayList<StatefulResourceProvider>();
+        this.authenticatedModifiable = new 
ArrayList<StatefulResourceProvider>();
+        this.resolver = resolver;
+        this.authInfo = authInfo;
+        this.securityTracker = securityTracker;
+    }
+
+    public void authenticateAll(List<ResourceProviderHandler> handlers) throws 
LoginException {
+        for (ResourceProviderHandler h : handlers) {
+            authenticate(h);
+        }
+    }
+
+    private StatefulResourceProvider authenticate(ResourceProviderHandler 
handler) throws LoginException {
+        StatefulResourceProvider rp = stateful.get(handler);
+        if (rp == null) {
+            rp = createStateful(handler);
+            if (rp == null) {
+                return null;
+            }
+            stateful.put(handler, rp);
+            if (handler.getInfo().getAuthType() != AuthType.no) {
+                authenticated.add(rp);
+            }
+            if (handler.getInfo().getModifiable()) {
+                authenticatedModifiable.add(rp);
+            }
+        }
+        return rp;
+    }
+
+    public StatefulResourceProvider getStateful(ResourceProviderHandler 
handler) {
+        try {
+            return authenticate(handler);
+        } catch (LoginException e) {
+            throw new SlingException("Can't authenticate provider", e);
+        }
+    }
+
+    public Collection<StatefulResourceProvider> getAllUsedAuthenticated() {
+        return authenticated;
+    }
+
+    public Collection<StatefulResourceProvider> getAllUsedModifiable() {
+        return authenticatedModifiable;
+    }
+
+    public Collection<StatefulResourceProvider> 
getAll(List<ResourceProviderHandler> handlers) {
+        List<StatefulResourceProvider> result = new 
ArrayList<StatefulResourceProvider>(handlers.size());
+        for (ResourceProviderHandler h : handlers) {
+            result.add(getStateful(h));
+        }
+        return result;
+    }
+
+    private StatefulResourceProvider createStateful(ResourceProviderHandler 
handler) throws LoginException {
+        ResourceProvider<?> rp = handler.getResourceProvider();
+        if (rp == null) {
+            logger.warn("Empty resource provider for {}", handler);
+            return null;
+        }
+        StatefulResourceProvider authenticated;
+        authenticated = new AuthenticatedResourceProvider(rp, 
handler.getInfo(), resolver, authInfo);
+        if (handler.getInfo().getUseResourceAccessSecurity()) {
+            authenticated = new SecureResourceProvider(authenticated, 
securityTracker);
+        }
+        return authenticated;
+    }
+
+}
\ No newline at end of file



Reply via email to