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;