You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2014/01/17 16:22:26 UTC

svn commit: r1559144 - in /sling/whiteboard/fmeschbe/featureflags: feature-flags/feature-flags-r1559093.diff resourceresolver/resourceresolver-r1559092.diff

Author: fmeschbe
Date: Fri Jan 17 15:22:25 2014
New Revision: 1559144

URL: http://svn.apache.org/r1559144
Log:
Diffs applied to this prototype

Added:
    sling/whiteboard/fmeschbe/featureflags/feature-flags/feature-flags-r1559093.diff
    sling/whiteboard/fmeschbe/featureflags/resourceresolver/resourceresolver-r1559092.diff

Added: sling/whiteboard/fmeschbe/featureflags/feature-flags/feature-flags-r1559093.diff
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/feature-flags-r1559093.diff?rev=1559144&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/feature-flags-r1559093.diff (added)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/feature-flags-r1559093.diff Fri Jan 17 15:22:25 2014
@@ -0,0 +1,601 @@
+Index: src/main/java/org/apache/sling/featureflags/Features.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/Features.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/Features.java	(Arbeitskopie)
+@@ -32,6 +32,16 @@
+ public interface Features {
+ 
+     /**
++     * Resource property of type String or String[] listing the features
++     * applicable to a resource.
++     * <p>
++     * If the ResourceResolver supports feature flags, the respective resource
++     * property is expected to be copied into the ResourceMetadata as a String[]
++     * property of this name.
++     */
++    String FEATURE_PROPERTY = "sling:features";
++
++    /**
+      * Get the list of all available feature names. A feature is available
+      * if there is a registered {@link Feature} service.
+      */
+Index: src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java	(Arbeitskopie)
+@@ -18,9 +18,6 @@
+  */
+ package org.apache.sling.featureflags.impl;
+ 
+-import org.apache.felix.scr.annotations.Component;
+-import org.apache.felix.scr.annotations.Reference;
+-import org.apache.felix.scr.annotations.Service;
+ import org.apache.sling.api.SlingHttpServletRequest;
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.featureflags.ClientContext;
+@@ -30,13 +27,14 @@
+ /**
+  * This is a wrapper around the internal feature manager.
+  */
+-@Component
+-@Service(value=Features.class)
+ public class FeaturesImpl implements Features {
+ 
+-    @Reference
+-    private FeatureManager manager;
++    private final FeatureManager manager;
+ 
++    FeaturesImpl(final FeatureManager manager) {
++        this.manager = manager;
++    }
++
+     @Override
+     public String[] getAvailableFeatureNames() {
+         return this.manager.getAvailableFeatureNames();
+Index: src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java	(Arbeitskopie)
+@@ -21,15 +21,11 @@
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+-import java.util.HashMap;
+ import java.util.List;
+-import java.util.Map;
+-
++import org.apache.sling.api.resource.ResourceDecorator;
+ import org.apache.sling.featureflags.ClientContext;
+ import org.apache.sling.featureflags.Feature;
+ import org.apache.sling.featureflags.ExecutionContext;
+-import org.apache.sling.featureflags.ResourceHiding;
+-import org.apache.sling.featureflags.ResourceTypeMapping;
+ 
+ /**
+  * Implementation of the client context
+@@ -40,25 +36,15 @@
+ 
+     private final List<Feature> enabledFeatures;
+ 
+-    private final List<ResourceHiding> hidingFeatures;
++    private final List<ResourceDecorator> mapperFeatures = new ArrayList<ResourceDecorator>();
+ 
+-    private final Map<String, String> mapperFeatures = new HashMap<String, String>();
+-
+     public ClientContextImpl(final ExecutionContext featureContext, final List<Feature> features) {
+         this.enabledFeatures = Collections.unmodifiableList(features);
+-        final List<ResourceHiding> hiding = new ArrayList<ResourceHiding>();
+-        for(final Feature f : this.enabledFeatures) {
+-            final ResourceHiding rh = f.adaptTo(ResourceHiding.class);
+-            if ( rh != null ) {
+-                hiding.add(rh);
++        for (final Feature f : this.enabledFeatures) {
++            if (f instanceof ResourceDecorator) {
++                mapperFeatures.add((ResourceDecorator) f);
+             }
+-            final ResourceTypeMapping rm = f.adaptTo(ResourceTypeMapping.class);
+-            if ( rm != null ) {
+-                final Map<String, String> mapping = rm.getResourceTypeMapping();
+-                mapperFeatures.putAll(mapping);
+-            }
+         }
+-        this.hidingFeatures = hiding;
+         this.featureContext = featureContext;
+     }
+ 
+@@ -81,11 +67,7 @@
+         return this.enabledFeatures;
+     }
+ 
+-    public Collection<ResourceHiding> getHidingFeatures() {
+-        return this.hidingFeatures;
+-    }
+-
+-    public Map<String, String> getResourceTypeMapping() {
++    public List<ResourceDecorator> getResourceDecorators() {
+         return this.mapperFeatures;
+     }
+ }
+Index: src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java	(Arbeitskopie)
+@@ -26,17 +26,22 @@
+ import java.util.Map;
+ import java.util.TreeMap;
+ 
++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.ReferenceCardinality;
+ import org.apache.felix.scr.annotations.ReferencePolicy;
+ import org.apache.sling.api.SlingHttpServletRequest;
++import org.apache.sling.api.resource.ResourceDecorator;
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.featureflags.ClientContext;
++import org.apache.sling.featureflags.ExecutionContext;
+ import org.apache.sling.featureflags.Feature;
+ import org.apache.sling.featureflags.Features;
+-import org.apache.sling.featureflags.ExecutionContext;
++import org.osgi.framework.BundleContext;
+ import org.osgi.framework.Constants;
++import org.osgi.framework.ServiceRegistration;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
+@@ -49,7 +54,7 @@
+            cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE,
+            policy=ReferencePolicy.DYNAMIC,
+            referenceInterface=Feature.class)
+-public class FeatureManager implements Features {
++public class FeatureManager {
+ 
+     private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ 
+@@ -57,6 +62,30 @@
+ 
+     private Map<String, FeatureDescription> activeFeatures = new TreeMap<String, FeatureDescription>();
+ 
++    private ServiceRegistration featuresService;
++
++    private ServiceRegistration resourceDecorator;
++
++    @Activate
++    private void activate(BundleContext bundleContext) {
++        this.featuresService = bundleContext.registerService(Features.class.getName(), new FeaturesImpl(this), null);
++        this.resourceDecorator = bundleContext.registerService(ResourceDecorator.class.getName(),
++            new FeatureResourceDecorator(this), null);
++    }
++
++    @Deactivate
++    private void deactivate() {
++        if (this.featuresService != null) {
++            this.featuresService.unregister();
++            this.featuresService = null;
++        }
++
++        if (this.resourceDecorator != null) {
++            this.resourceDecorator.unregister();
++            this.resourceDecorator = null;
++        }
++    }
++
+     /**
+      * Bind a new feature
+      */
+@@ -124,7 +153,6 @@
+         }
+     };
+ 
+-    @Override
+     public ClientContext getCurrentClientContext() {
+         ClientContext result = perThreadClientContext.get();
+         if ( result == null ) {
+@@ -143,7 +171,6 @@
+         perThreadClientContext.remove();
+     }
+ 
+-    @Override
+     public ClientContext createClientContext(final ResourceResolver resolver) {
+         if ( resolver == null ) {
+             throw new IllegalArgumentException("Resolver must not be null.");
+@@ -153,7 +180,6 @@
+         return ctx;
+     }
+ 
+-    @Override
+     public ClientContext createClientContext(final SlingHttpServletRequest request) {
+         if ( request == null ) {
+             throw new IllegalArgumentException("Request must not be null.");
+@@ -178,7 +204,6 @@
+         return ctx;
+     }
+ 
+-    @Override
+     public Feature[] getAvailableFeatures() {
+         final List<Feature> result = new ArrayList<Feature>();
+         for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
+@@ -188,7 +213,6 @@
+         return result.toArray(new Feature[result.size()]);
+     }
+ 
+-    @Override
+     public Feature getFeature(final String name) {
+         final FeatureDescription desc = this.activeFeatures.get(name);
+         if ( desc != null ) {
+@@ -197,12 +221,10 @@
+         return null;
+     }
+ 
+-    @Override
+     public String[] getAvailableFeatureNames() {
+         return this.activeFeatures.keySet().toArray(new String[this.activeFeatures.size()]);
+     }
+ 
+-    @Override
+     public boolean isAvailable(final String featureName) {
+         return this.activeFeatures.containsKey(featureName);
+     }
+Index: src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java	(Arbeitskopie)
+@@ -1,58 +0,0 @@
+-/*
+- * 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.featureflags.impl;
+-
+-import org.apache.felix.scr.annotations.Component;
+-import org.apache.felix.scr.annotations.Property;
+-import org.apache.felix.scr.annotations.Reference;
+-import org.apache.felix.scr.annotations.Service;
+-import org.apache.sling.api.resource.Resource;
+-import org.apache.sling.featureflags.ClientContext;
+-import org.apache.sling.featureflags.ResourceHiding;
+-import org.apache.sling.resourceaccesssecurity.AllowingResourceAccessGate;
+-import org.apache.sling.resourceaccesssecurity.ResourceAccessGate;
+-
+-/**
+- * Resource access gate implementing the hiding of resources.
+- */
+-@Component
+-@Service(value=ResourceAccessGate.class)
+-@Property(name=ResourceAccessGate.CONTEXT, value=ResourceAccessGate.APPLICATION_CONTEXT)
+-public class ResourceAccessImpl
+-    extends AllowingResourceAccessGate
+-    implements ResourceAccessGate {
+-
+-    @Reference
+-    private FeatureManager manager;
+-
+-    @Override
+-    public GateResult canRead(final Resource resource) {
+-        boolean available = true;
+-        final ClientContext info = manager.getCurrentClientContext();
+-        if ( info != null ) {
+-            for(final ResourceHiding f : ((ClientContextImpl)info).getHidingFeatures() ) {
+-                available = !f.hideResource(resource);
+-                if ( !available) {
+-                    break;
+-                }
+-            }
+-        }
+-        return (available ? GateResult.DONTCARE : GateResult.DENIED);
+-    }
+-}
+Index: src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java	(Arbeitskopie)
+@@ -1,69 +0,0 @@
+-/*
+- * 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.featureflags.impl;
+-
+-import javax.servlet.http.HttpServletRequest;
+-
+-import org.apache.felix.scr.annotations.Component;
+-import org.apache.felix.scr.annotations.Reference;
+-import org.apache.felix.scr.annotations.Service;
+-import org.apache.sling.api.resource.Resource;
+-import org.apache.sling.api.resource.ResourceDecorator;
+-import org.apache.sling.api.resource.ResourceWrapper;
+-import org.apache.sling.featureflags.ClientContext;
+-
+-/**
+- * Resource decorator implementing the resource type mapping
+- */
+-@Component
+-@Service(value=ResourceDecorator.class)
+-public class ResourceDecoratorImpl implements ResourceDecorator {
+-
+-    @Reference
+-    private FeatureManager manager;
+-
+-    @Override
+-    public Resource decorate(final Resource resource) {
+-        final ClientContext info = manager.getCurrentClientContext();
+-        if ( info != null ) {
+-            final String resourceType = resource.getResourceType();
+-            final String overwriteType = ((ClientContextImpl)info).getResourceTypeMapping().get(resourceType);
+-            if ( overwriteType != null ) {
+-                return new ResourceWrapper(resource) {
+-
+-                    @Override
+-                    public String getResourceType() {
+-                        return overwriteType;
+-                    }
+-
+-                    @Override
+-                    public String getResourceSuperType() {
+-                        return resourceType;
+-                    }
+-                };
+-            }
+-        }
+-        return resource;
+-    }
+-
+-    @Override
+-    public Resource decorate(final Resource resource, final HttpServletRequest request) {
+-        return this.decorate(resource);
+-    }
+-}
+Index: src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java	(Revision 0)
++++ src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java	(Revision 1559139)
+@@ -0,0 +1,57 @@
++/*
++ * 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.featureflags.impl;
++
++import javax.servlet.http.HttpServletRequest;
++
++import org.apache.sling.api.resource.Resource;
++import org.apache.sling.api.resource.ResourceDecorator;
++import org.apache.sling.featureflags.ClientContext;
++
++/**
++ * Resource decorator implementing the resource type mapping
++ */
++public class FeatureResourceDecorator implements ResourceDecorator {
++
++    private final FeatureManager manager;
++
++    FeatureResourceDecorator(final FeatureManager manager) {
++        this.manager = manager;
++    }
++
++    @Override
++    public Resource decorate(final Resource resource) {
++        Resource result = resource;
++        final ClientContext info = manager.getCurrentClientContext();
++        if (info instanceof ClientContextImpl) {
++            for (ResourceDecorator rd : ((ClientContextImpl) info).getResourceDecorators()) {
++                Resource r = rd.decorate(resource);
++                if (r != null) {
++                    result = r;
++                }
++            }
++        }
++        return result;
++    }
++
++    @Override
++    public Resource decorate(final Resource resource, final HttpServletRequest request) {
++        return this.decorate(resource);
++    }
++}
+
+Eigenschaftsänderungen: src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Index: src/main/java/org/apache/sling/featureflags/ClientContext.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/ClientContext.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/ClientContext.java	(Arbeitskopie)
+@@ -31,7 +31,12 @@
+ public interface ClientContext {
+ 
+     /**
+-     * Returns <code>true</code> if the feature is enabled.
++     * Returns {@code true} if the named feature is enabled.
++     *
++     * @param featureName The name of the feature.
++     * @return {@code true} if the named feature is enabled. {@code false} is
++     *         returned if the named feature is not enabled, is not known or the
++     *         {@code featureName} parameter is {@code null} or an empty String.
+      */
+     boolean isEnabled(String featureName);
+ 
+Index: src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java	(Arbeitskopie)
+@@ -1,41 +0,0 @@
+-/*
+- * 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.featureflags;
+-
+-import java.util.Map;
+-
+-import aQute.bnd.annotation.ConsumerType;
+-
+-/**
+- * A {@link Feature} which is mapping resource types can be adapted to
+- * this service interface.
+- */
+-@ConsumerType
+-public interface ResourceTypeMapping {
+-
+-    /**
+-     * Returns the resource type mapping for a feature.
+-     * This mapping is only executed if {@link Feature#isEnabled(ExecutionContext)}
+-     * return true for the given feature/context. The caller of this
+-     * method must ensure to call {@link Feature#isEnabled(ExecutionContext)}
+-     * before calling this method and only call this method if
+-     * {@link Feature#isEnabled(ExecutionContext)} returned <code>true</code>
+-     */
+-    Map<String, String> getResourceTypeMapping();
+-}
+Index: src/main/java/org/apache/sling/featureflags/ResourceHiding.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/ResourceHiding.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/ResourceHiding.java	(Arbeitskopie)
+@@ -1,41 +0,0 @@
+-/*
+- * 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.featureflags;
+-
+-import org.apache.sling.api.resource.Resource;
+-
+-import aQute.bnd.annotation.ConsumerType;
+-
+-/**
+- * A {@link Feature} which is hiding resources can be adapted to
+- * this service interface.
+- */
+-@ConsumerType
+-public interface ResourceHiding {
+-
+-    /**
+-     * Checks whether a resource should be hidden for a feature.
+-     * This check is only executed if {@link Feature#isEnabled(ExecutionContext)}
+-     * return true for the given feature/context. The caller of this
+-     * method must ensure to call {@link Feature#isEnabled(ExecutionContext)}
+-     * before calling this method and only call this method if
+-     * {@link Feature#isEnabled(ExecutionContext)} returned <code>true</code>
+-     */
+-    boolean hideResource(Resource resource);
+-}
+Index: src/main/java/org/apache/sling/featureflags/Feature.java
+===================================================================
+--- src/main/java/org/apache/sling/featureflags/Feature.java	(Revision 1559093)
++++ src/main/java/org/apache/sling/featureflags/Feature.java	(Arbeitskopie)
+@@ -18,23 +18,15 @@
+  */
+ package org.apache.sling.featureflags;
+ 
+-import org.apache.sling.api.adapter.Adaptable;
+-
+ import aQute.bnd.annotation.ConsumerType;
+ 
+ /**
+  * A feature is defined by its name.
+- * Depending on the functionality the feature implements it can
+- * be adapted to different services, like
+- * <ul>
+- *   <li>{@link ResourceHiding}</li>
+- *   <li>{@link ResourceTypeMapping}</li>
+- * </ul>
+  *
+  * Features are registered as OSGi services.
+  */
+ @ConsumerType
+-public interface Feature extends Adaptable {
++public interface Feature {
+ 
+     /**
+      * Checks whether the feature is enabled for the current execution
+Index: pom.xml
+===================================================================
+--- pom.xml	(Revision 1559093)
++++ pom.xml	(Arbeitskopie)
+@@ -48,12 +48,6 @@
+         </dependency>
+         <dependency>
+             <groupId>org.apache.sling</groupId>
+-            <artifactId>org.apache.sling.resourceaccesssecurity</artifactId>
+-            <version>0.0.1-SNAPSHOT</version>
+-            <scope>provided</scope>
+-        </dependency>
+-        <dependency>
+-            <groupId>org.apache.sling</groupId>
+             <artifactId>org.apache.sling.commons.osgi</artifactId>
+             <version>2.2.0</version>
+             <scope>provided</scope>

Added: sling/whiteboard/fmeschbe/featureflags/resourceresolver/resourceresolver-r1559092.diff
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/resourceresolver/resourceresolver-r1559092.diff?rev=1559144&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/resourceresolver/resourceresolver-r1559092.diff (added)
+++ sling/whiteboard/fmeschbe/featureflags/resourceresolver/resourceresolver-r1559092.diff Fri Jan 17 15:22:25 2014
@@ -0,0 +1,524 @@
+Index: pom.xml
+===================================================================
+--- pom.xml	(Revision 1559092)
++++ pom.xml	(Arbeitskopie)
+@@ -78,6 +78,7 @@
+                 <configuration>
+                     <instructions>
+                         <Import-Package>
++                            org.apache.sling.featureflags;resolution:=optional,
+                             javax.annotation;resolution:=optional,
+                             org.apache.sling.api.resource;provide:=true,
+                             org.apache.sling.commons.osgi;version="$(@)",
+@@ -87,6 +88,9 @@
+                             org.apache.sling.resourceresolver.impl.*,
+                             org.apache.sling.resourceresolver.accessgate.impl.*
+                         </Private-Package>
++                        <DynamicImport-Package>
++                            org.apache.sling.featureflags;version="[1.0,2.0)"
++                        </DynamicImport-Package>
+                     </instructions>
+                 </configuration>
+             </plugin>
+@@ -145,6 +149,14 @@
+             <scope>provided</scope>
+         </dependency>
+ 
++        <!-- Feature Flag Support -->
++        <dependency>
++            <groupId>org.apache.sling</groupId>
++            <artifactId>org.apache.sling.featureflags</artifactId>
++            <version>0.0.1-SNAPSHOT</version>
++            <scope>provided</scope>
++        </dependency>
++
+         <!-- For the Console Plugin of the ResourceResolverFactoryImpl -->
+         <dependency>
+             <groupId>javax.servlet</groupId>
+Index: src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java	(Revision 1559092)
++++ src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java	(Arbeitskopie)
+@@ -122,7 +122,7 @@
+ 
+         // create new context
+         final ResourceResolverContext newContext = new ResourceResolverContext(this.context.isAdmin(),
+-                newAuthenticationInfo, factory.getResourceAccessSecurityTracker() );
++                newAuthenticationInfo, factory.getResourceAccessSecurityTracker(), this.context.getFeaturesHolder() );
+         this.factory.getRootProviderEntry().loginToRequiredFactories(newContext);
+ 
+         // create a regular resource resolver
+Index: src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java	(Revision 1559092)
++++ src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java	(Arbeitskopie)
+@@ -100,7 +100,7 @@
+                     final boolean isAdmin)
+     throws LoginException {
+         // create context
+-        final ResourceResolverContext ctx = new ResourceResolverContext(isAdmin, authenticationInfo, this.activator.getResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = new ResourceResolverContext(isAdmin, authenticationInfo, this.activator.getResourceAccessSecurityTracker(), this.activator);
+ 
+         // login
+         this.activator.getRootProviderEntry().loginToRequiredFactories(ctx);
+Index: src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java	(Revision 1559092)
++++ src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java	(Arbeitskopie)
+@@ -41,6 +41,8 @@
+ import org.apache.sling.api.resource.ResourceProviderFactory;
+ import org.apache.sling.api.resource.ResourceResolverFactory;
+ import org.apache.sling.commons.osgi.PropertiesUtil;
++import org.apache.sling.featureflags.Features;
++import org.apache.sling.resourceresolver.impl.helper.FeaturesHolder;
+ import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+ import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+ import org.apache.sling.resourceresolver.impl.mapping.Mapping;
+@@ -76,7 +78,7 @@
+     @Reference(name = "ResourceProvider", referenceInterface = ResourceProvider.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
+     @Reference(name = "ResourceProviderFactory", referenceInterface = ResourceProviderFactory.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
+     @Reference(name = "ResourceDecorator", referenceInterface = ResourceDecorator.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC) })
+-public class ResourceResolverFactoryActivator {
++public class ResourceResolverFactoryActivator implements FeaturesHolder {
+ 
+     private static final class FactoryRegistration {
+         /** Registration .*/
+@@ -219,6 +221,11 @@
+     @Reference
+     ResourceAccessSecurityTracker resourceAccessSecurityTracker;
+ 
++    @Reference(
++            policy = ReferencePolicy.DYNAMIC,
++            cardinality = ReferenceCardinality.OPTIONAL_UNARY)
++    private Features featuresService;
++
+     /** ComponentContext */
+     private volatile ComponentContext componentContext;
+ 
+@@ -243,6 +250,10 @@
+         return this.resourceAccessSecurityTracker;
+     }
+ 
++    public Features getFeatures() {
++        return this.featuresService;
++    }
++
+     public EventAdmin getEventAdmin() {
+         return this.eventAdmin;
+     }
+Index: src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java	(Revision 1559092)
++++ src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java	(Arbeitskopie)
+@@ -32,6 +32,9 @@
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.api.resource.ResourceResolverFactory;
+ import org.apache.sling.api.resource.ResourceUtil;
++import org.apache.sling.api.resource.ValueMap;
++import org.apache.sling.featureflags.ClientContext;
++import org.apache.sling.featureflags.Features;
+ import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
+ 
+ /**
+@@ -67,13 +70,17 @@
+     /** Resource type resource resolver (admin resolver) */
+     private ResourceResolver resourceTypeResourceResolver;
+ 
++    /** Features ServiceTracker */
++    private final FeaturesHolder featuresService;
++
+     /**
+      * Create a new resource resolver context.
+      */
+-    public ResourceResolverContext(final boolean isAdmin, final Map<String, Object> originalAuthInfo, final ResourceAccessSecurityTracker resourceAccessSecurityTracker) {
++    public ResourceResolverContext(final boolean isAdmin, final Map<String, Object> originalAuthInfo, final ResourceAccessSecurityTracker resourceAccessSecurityTracker, final FeaturesHolder featuresService) {
+         this.isAdmin = isAdmin;
+         this.originalAuthInfo = originalAuthInfo;
+         this.resourceAccessSecurityTracker = resourceAccessSecurityTracker;
++        this.featuresService = featuresService;
+     }
+ 
+     /**
+@@ -84,6 +91,13 @@
+     }
+ 
+     /**
++     * @return the Features service tracker used by this context
++     */
++    public FeaturesHolder getFeaturesHolder() {
++        return this.featuresService;
++    }
++
++    /**
+      * Return the authentication info.
+      */
+     public Map<String, Object> getAuthenticationInfo() {
+@@ -235,4 +249,51 @@
+         }
+         return resourceSuperType;
+     }
++
++    /**
++     * Checks whether the resource has a sling:features property. If so the
++     * Features service is consulted to indicate whether any of the named
++     * features is enabled and thus the resource is visible at all.
++     * <p>
++     * If none of the listed features is enabled, the resource is not visible
++     * and null is returned. Otherwise the list of features configured on the
++     * Resource is stored in the ResourceMetadata and the resource is returned.
++     * <p>
++     * If the Features service is not available, the features property is not
++     * consulted and not added to the ResourceMetadata. The resource is returned
++     * unmodified in this case.
++     *
++     * @param resource The resource to check for features
++     * @return The unmodified resource if the Features service is not available
++     *         or no features are set on the property. Otherwise the features
++     *         are added to the ResourceMetadata and the resource is returned if
++     *         one of the listed features is enabled. Otherwise null is
++     *         returned.
++     */
++    public Resource applyFeatures(final Resource resource) {
++        if (resource != null) {
++            Features featuresService = this.featuresService.getFeatures();
++            if (featuresService != null) {
++                ValueMap props = resource.adaptTo(ValueMap.class);
++                if (props != null) {
++                    ClientContext featureContext = featuresService.getCurrentClientContext();
++                    String[] features = props.get(Features.FEATURE_PROPERTY, String[].class);
++                    if (features != null && features.length > 0) {
++                        for (String feature : features) {
++                            if (featureContext.isEnabled(feature)) {
++                                resource.getResourceMetadata().put(Features.FEATURE_PROPERTY, features);
++                                return resource;
++                            }
++                        }
++
++                        // invariant: none of the named features enabled
++                        return null;
++                    }
++                }
++            }
++        }
++
++        // invariant: null resource or no feature check on resource
++        return resource;
++    }
+ }
+Index: src/main/java/org/apache/sling/resourceresolver/impl/helper/FeaturesHolder.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/helper/FeaturesHolder.java	(Revision 0)
++++ src/main/java/org/apache/sling/resourceresolver/impl/helper/FeaturesHolder.java	(Revision 1559139)
+@@ -0,0 +1,35 @@
++/*
++ * 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.helper;
++
++import org.apache.sling.featureflags.Features;
++
++/**
++ * The <code>FeaturesHolder</code> interface is an API to provide dynamic
++ * access to the Features service without having to hold on to the
++ * Features instance directly.
++ */
++public interface FeaturesHolder {
++
++    /**
++     * @return The {@code Features} service if available, {@code null} otherwise
++     */
++    public Features getFeatures();
++
++}
+
+Eigenschaftsänderungen: src/main/java/org/apache/sling/resourceresolver/impl/helper/FeaturesHolder.java
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Index: src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
+===================================================================
+--- src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java	(Revision 1559092)
++++ src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java	(Arbeitskopie)
+@@ -176,7 +176,8 @@
+                 returnValue = resourceAccessSecurity.getReadableResource(resource);
+             }
+         }
+-        return returnValue;
++
++        return ctx.applyFeatures(returnValue);
+     }
+ 
+     /**
+Index: src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java	(Arbeitskopie)
+@@ -37,6 +37,7 @@
+ import org.apache.sling.api.resource.ResourceProvider;
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.api.resource.ValueMap;
++import org.apache.sling.resourceresolver.impl.helper.MockFeaturesHolder;
+ import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+ import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+ import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
+@@ -132,7 +133,7 @@
+                 return rrp;
+             }
+         };
+-        resolver = new ResourceResolverImpl(crf, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++        resolver = new ResourceResolverImpl(crf, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), MockFeaturesHolder.INSTANCE));
+     }
+ 
+     protected void assertExistent(Resource r, boolean existent) {
+Index: src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java	(Arbeitskopie)
+@@ -41,6 +41,7 @@
+ import org.apache.sling.api.resource.ResourceResolver;
+ import org.apache.sling.api.resource.ResourceResolverFactory;
+ import org.apache.sling.api.resource.SyntheticResource;
++import org.apache.sling.resourceresolver.impl.helper.MockFeaturesHolder;
+ import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+ import org.junit.Before;
+ import org.junit.Test;
+@@ -57,11 +58,11 @@
+     @Before public void setup() {
+         commonFactory = new CommonResourceResolverFactoryImpl(new ResourceResolverFactoryActivator());
+         resFac = new ResourceResolverFactoryImpl(commonFactory, /* TODO: using Bundle */ null, null);
+-        resResolver = new ResourceResolverImpl(commonFactory, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++        resResolver = new ResourceResolverImpl(commonFactory, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), MockFeaturesHolder.INSTANCE));
+     }
+ 
+     @Test public void testClose() throws Exception {
+-        final ResourceResolver rr = new ResourceResolverImpl(commonFactory, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++        final ResourceResolver rr = new ResourceResolverImpl(commonFactory, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), MockFeaturesHolder.INSTANCE));
+         assertTrue(rr.isLive());
+         rr.close();
+         assertFalse(rr.isLive());
+@@ -358,7 +359,7 @@
+                     }
+ 
+                 },
+-                new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++                new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), null));
+         resolvers.add(resolver);
+ 
+         // the resources to test
+@@ -394,7 +395,7 @@
+                     }
+ 
+                 },
+-                new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++                new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), null));
+         resolvers.add(resolver);
+         final Resource r = new SyntheticResource(resolver, "/a", "a:b") {
+             @Override
+Index: src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java	(Arbeitskopie)
+@@ -24,6 +24,7 @@
+ import javax.jcr.Session;
+ 
+ import org.apache.sling.api.resource.Resource;
++import org.apache.sling.resourceresolver.impl.helper.MockFeaturesHolder;
+ import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+ import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
+ import org.junit.Before;
+@@ -77,7 +78,7 @@
+             }
+         };
+ 
+-        rr = new ResourceResolverImpl(fac, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker()));
++        rr = new ResourceResolverImpl(fac, new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), MockFeaturesHolder.INSTANCE));
+     }
+ 
+     @Test
+Index: src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecorationTest.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecorationTest.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecorationTest.java	(Arbeitskopie)
+@@ -18,9 +18,9 @@
+  */
+ package org.apache.sling.resourceresolver.impl;
+ 
+-import static org.junit.Assert.assertNull;
+ import static org.junit.Assert.assertEquals;
+ import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertNull;
+ import static org.junit.Assert.assertTrue;
+ 
+ import java.util.Iterator;
+Index: src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java	(Arbeitskopie)
+@@ -100,7 +100,7 @@
+         final AdaptableResourceProviderImpl rp3 = new AdaptableResourceProviderImpl(new String[] {"/hello"}, 3L);
+         final ResourceProviderImpl rp4 = new ResourceProviderImpl(new String[] {"/you"}, 4L);
+ 
+-        final ResourceResolverContext ctx = new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), MockFeaturesHolder.INSTANCE);
+ 
+         final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+         check(spl, ctx);
+@@ -132,7 +132,7 @@
+         final AdaptableResourceProviderImpl rp4 = new AdaptableResourceProviderImpl(new String[] {"/a/a"}, 4L);
+         final AdaptableResourceProviderImpl rp5 = new AdaptableResourceProviderImpl(new String[] {"/all/or/nothing"}, 5L);
+ 
+-        final ResourceResolverContext ctx = new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = new ResourceResolverContext(false, null, new ResourceAccessSecurityTracker(), null);
+ 
+         final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+         check(spl, ctx);
+Index: src/test/java/org/apache/sling/resourceresolver/impl/helper/MockFeaturesHolder.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/helper/MockFeaturesHolder.java	(Revision 0)
++++ src/test/java/org/apache/sling/resourceresolver/impl/helper/MockFeaturesHolder.java	(Revision 1559139)
+@@ -0,0 +1,29 @@
++/*
++ * 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.helper;
++
++import org.apache.sling.featureflags.Features;
++
++public class MockFeaturesHolder implements FeaturesHolder {
++    public static final FeaturesHolder INSTANCE = new MockFeaturesHolder();
++
++    public Features getFeatures() {
++        return null;
++    }
++}
+
+Eigenschaftsänderungen: src/test/java/org/apache/sling/resourceresolver/impl/helper/MockFeaturesHolder.java
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Index: src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
+===================================================================
+--- src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java	(Revision 1559092)
++++ src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java	(Arbeitskopie)
+@@ -37,6 +37,8 @@
+ import org.junit.Before;
+ import org.junit.Test;
+ import org.mockito.Mockito;
++import org.mockito.invocation.InvocationOnMock;
++import org.mockito.stubbing.Answer;
+ import org.osgi.framework.Constants;
+ 
+ public class ResourceProviderEntryTest {
+@@ -56,8 +58,7 @@
+ 
+     @Test public void testRootProvider() {
+         assertNull(root.getResource(null, null, "relpath"));
+-        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+-        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = getResourceResolverContext();
+         assertEqualsResolver(this.rootResolver, root.getResource(ctx, null, "/"));
+         assertEqualsResolver(this.rootResolver, root.getResource(ctx, null, "/rootel"));
+         assertEqualsResolver(this.rootResolver, root.getResource(ctx, null, "/rootel/child"));
+@@ -68,8 +69,7 @@
+ 
+     @Test public void testAdd1Provider() {
+         String firstPath = "/rootel";
+-        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+-        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = getResourceResolverContext();
+         final ResourceResolver resolver = Mockito.mock(ResourceResolver.class);
+         final ResourceProvider first = Mockito.mock(ResourceProvider.class);
+         Mockito.when(first.getResource(Mockito.any(ResourceResolver.class), Mockito.startsWith(firstPath))).thenReturn(new TestResource(resolver));
+@@ -95,8 +95,7 @@
+         String thirdPath = "/apps/sling/sample";
+         String secondPath = firstPath + "/child";
+ 
+-        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+-        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = getResourceResolverContext();
+         final ResourceResolver firstResolver = Mockito.mock(ResourceResolver.class);
+         final ResourceProvider first = Mockito.mock(ResourceProvider.class);
+         Mockito.when(first.getResource(Mockito.any(ResourceResolver.class), Mockito.startsWith(firstPath))).thenReturn(new TestResource(firstResolver));
+@@ -135,8 +134,7 @@
+         String thirdPath = "/apps/sling/sample";
+         String secondPath = firstPath + "/child";
+ 
+-        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+-        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = getResourceResolverContext();
+         final ResourceResolver firstResolver = Mockito.mock(ResourceResolver.class);
+         final ResourceProvider first = Mockito.mock(ResourceProvider.class);
+         Mockito.when(first.getResource(Mockito.any(ResourceResolver.class), Mockito.startsWith(firstPath))).thenReturn(new TestResource(firstResolver));
+@@ -175,8 +173,7 @@
+         String thirdPath = "/apps/sling/sample";
+         String secondPath = firstPath + "/child";
+ 
+-        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+-        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        final ResourceResolverContext ctx = getResourceResolverContext();
+         final ResourceResolver firstResolver = Mockito.mock(ResourceResolver.class);
+         final ResourceProvider first = Mockito.mock(ResourceProvider.class);
+         Mockito.when(first.getResource(Mockito.any(ResourceResolver.class), Mockito.startsWith(firstPath))).thenReturn(new TestResource(firstResolver));
+@@ -223,9 +220,8 @@
+         for(String path : new String[] { "/foo", "/", "/foo/bar" }) {
+             final ResourceResolver resolver = Mockito.mock(ResourceResolver.class);
+             final ResourceProvider p = Mockito.mock(ResourceProvider.class);
+-            final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
+             Mockito.when(p.getResource(Mockito.any(ResourceResolver.class), Mockito.startsWith(path))).thenReturn(new TestResource(resolver));
+-            Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++            final ResourceResolverContext ctx = getResourceResolverContext();
+ 
+             final Map<String, Object> props = new HashMap<String, Object>();
+             props.put(Constants.SERVICE_ID, ++counter);
+@@ -256,6 +252,17 @@
+         assertEquals(resolver, res.getResourceResolver());
+     }
+ 
++    private ResourceResolverContext getResourceResolverContext() {
++        final ResourceResolverContext ctx = Mockito.mock(ResourceResolverContext.class);
++        Mockito.when(ctx.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker());
++        Mockito.when(ctx.applyFeatures(Mockito.any(Resource.class))).then(new Answer<Resource>() {
++            public Resource answer(InvocationOnMock invocation) {
++                return (Resource) invocation.getArguments()[0];
++            }
++        });
++        return ctx;
++    }
++
+     private static class TestResource extends AbstractResource {
+ 
+         private final ResourceResolver resourceResolver;