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/28 09:32:40 UTC
svn commit: r1561994 - in /sling/trunk/contrib/extensions/feature-flags: ./
src/main/java/org/apache/sling/featureflags/
src/main/java/org/apache/sling/featureflags/impl/
Author: fmeschbe
Date: Tue Jan 28 08:32:39 2014
New Revision: 1561994
URL: http://svn.apache.org/r1561994
Log:
SLING-3148 Merge back from whiteboard/fmeschbe/featureflags/feature-flags
- Removed ResourceHiding and ResourceTypeHiding
- Added ResourceDecorator support
- Added WebConsole plugin
- Added Feature factory configuration support
- Added JavaDoc
- Support non-Sling ExecutionContext
- Porperly register ClientContext filter as Sling filter
Added:
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ConfiguredFeature.java
- copied unchanged from r1561989, sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ConfiguredFeature.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
- copied unchanged from r1561989, sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
- copied unchanged from r1561989, sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
Removed:
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ResourceHiding.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java
Modified:
sling/trunk/contrib/extensions/feature-flags/ (props changed)
sling/trunk/contrib/extensions/feature-flags/pom.xml
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java
Propchange: sling/trunk/contrib/extensions/feature-flags/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Tue Jan 28 08:32:39 2014
@@ -0,0 +1 @@
+/sling/whiteboard/fmeschbe/featureflags/feature-flags:1559093-1561989
Modified: sling/trunk/contrib/extensions/feature-flags/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/pom.xml?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/pom.xml (original)
+++ sling/trunk/contrib/extensions/feature-flags/pom.xml Tue Jan 28 08:32:39 2014
@@ -43,19 +43,13 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.api</artifactId>
- <version>2.5.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.resourceaccesssecurity</artifactId>
- <version>0.0.1-SNAPSHOT</version>
+ <version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.osgi</artifactId>
- <version>2.2.0</version>
+ <version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java Tue Jan 28 08:32:39 2014
@@ -23,21 +23,31 @@ import java.util.Collection;
import aQute.bnd.annotation.ProviderType;
/**
- * The client context can be used by client code to check whether
- * a specific feature is enable.
- * A client context can be created through the {@link Features} service.
+ * The client context can be used by client code to check whether a specific
+ * feature is enable.
+ * <p>
+ * Prepared {@code ClientContext} instances are available through the
+ * {@link Features} service. Consumers of this interface are not expected to
+ * implent it.
*/
@ProviderType
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);
/**
- * Returns a list of all enabled features
- * @return The list of features, the list might be empty.
+ * Returns a possibly empty collection of enabled {@link Feature} instances.
+ *
+ * @return The collection of enabled {@link Feature} instances. This
+ * collection may be empty and is not modifiable.
*/
Collection<Feature> getEnabledFeatures();
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java Tue Jan 28 08:32:39 2014
@@ -18,26 +18,49 @@
*/
package org.apache.sling.featureflags;
-import org.apache.sling.api.SlingHttpServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.resource.ResourceResolver;
import aQute.bnd.annotation.ProviderType;
/**
- * The provider context contains all information that is passed to a
- * {@link Feature} in order to check whether a feature is enabled.
+ * The {@code ExecutionContext} interface provides access to the context for
+ * evaluating whether a feature is enabled or not. Instances of this object are
+ * provided to the {@link Feature#isEnabled(ExecutionContext)} to help
+ * evaluating whether the feature is enabled or not.
+ * <p>
+ * The {@link Features} service {@link ClientContext} generating methods create
+ * an instance of this to collect the enabled {@link Feature} services for the
+ * creation of the {@link ClientContext} instance.
+ * <p>
+ * This object provides access to live data and must only be used to read
+ * information. Modifying content through a {@code ResourceResolver} directly or
+ * indirectly provided by this object is considered inappropriate and faulty
+ * behaviour.
*/
@ProviderType
public interface ExecutionContext {
/**
- * Return the associated request if available
+ * Returns a {@code HttpServletRequest} object to retrieve information which
+ * may influence the decision whether a {@link Feature} is enabled or not.
+ * If a {@code HttpServletRequest} object is not available in the context,
+ * this method may return {@code null}.
+ * <p>
+ * In a Sling request processing context, the {@code HttpServletRequest}
+ * object returned may actually be a {@code SlingHttpServletRequest}.
+ *
* @return the request or <code>null</code>
*/
- SlingHttpServletRequest getRequest();
+ HttpServletRequest getRequest();
/**
- * Return the associated resource resolver.
+ * Returns a {@code ResourceResolver} object to retrieve information which
+ * may influence the decision whether a {@link Feature} is enabled or not.
+ * If a {@code ResourceResolver} object is not available in the context,
+ * this method may return {@code null}.
+ *
* @return the resource resolver
*/
ResourceResolver getResourceResolver();
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java Tue Jan 28 08:32:39 2014
@@ -18,37 +18,47 @@
*/
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.
+ * A feature is defined by its name. Features are registered as OSGi services.
+ * This interface is expected to be implemented by feature providers.
+ * <p>
+ * Feature {@link #getName() names} should be globally unique. If multiple
+ * features have the same name, the feature with the highest service ranking is
+ * accessible through the {@link Features} service and the {@link ClientContext}.
*/
@ConsumerType
-public interface Feature extends Adaptable {
+public interface Feature {
/**
- * Checks whether the feature is enabled for the current execution
- * context.
+ * Checks whether the feature is enabled for the given execution context.
+ * <p>
+ * Multiple calls to this method may but are not required to return the same
+ * value. For example the return value may depend on the time of day, some
+ * random number or some information provided by the given
+ * {@link ExecutionContext}.
+ *
+ * @param context The {@link ExecutionContext} providing a context to
+ * evaluate whether the feature is enabled or not.
+ * @return {@code true} if this {@code Feature} is enabled in the given
+ * {@link ExecutionContext}.
*/
boolean isEnabled(ExecutionContext context);
/**
* The name of the feature.
+ *
+ * @return The name of this feature which must not be {@code null} or an
+ * empty string.
*/
String getName();
/**
* The description of the feature.
+ *
+ * @return The optional description of this feature, which may be
+ * {@code null} or an empty string.
*/
String getDescription();
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java Tue Jan 28 08:32:39 2014
@@ -18,59 +18,108 @@
*/
package org.apache.sling.featureflags;
-import org.apache.sling.api.SlingHttpServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.resource.ResourceResolver;
import aQute.bnd.annotation.ProviderType;
/**
- * The features service is the central gateway for feature handling.
- * It can be used to query the available features and to create
- * client contexts to be used for enabled feature checking.
+ * The features service is the central gateway for feature handling. It can be
+ * used to query the available features and to create client contexts to be used
+ * for enabled feature checking.
*/
@ProviderType
public interface Features {
/**
- * Get the list of all available feature names. A feature is available
- * if there is a registered {@link Feature} service.
+ * Get the list of all available (known) feature names.
+ * <p>
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @return The names of the known features
*/
String[] getAvailableFeatureNames();
/**
- * Get the list of all available features. A feature is available
- * if there is a registered {@link Feature} service.
+ * Get the list of all available (known) features.
+ * <p>
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @return The known features
*/
Feature[] getAvailableFeatures();
/**
* Returns the feature with the given name.
- * @return The feature or <code>null</code>
+ *
+ * @param name The name of the feature.
+ * @return The feature or <code>null</code> if not known or the name is an
+ * empty string or {@code null}.
*/
Feature getFeature(String name);
/**
- * Checks whether a feature with the given name is available.
- * A feature is available if there is a registered {@link Feature} service.
+ * Checks whether a feature with the given name is available (known).
+ * <p>
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @param featureName The name of the feature to check for availability.
+ * @return {@code true} if the named feature is available.
*/
boolean isAvailable(String featureName);
/**
- * Returns the current client context.
- * This method always returns a client context object
+ * Returns the current client context. This method always returns a client
+ * context object.
+ *
* @return A client context.
*/
ClientContext getCurrentClientContext();
/**
* Create a client context for the resource resolver.
- * @throws IllegalArgumentException If resolver is null
+ * <p>
+ * The {@link ClientContext} is a snapshot of the enablement state of the
+ * features at the time of creation. A change in the feature enablement
+ * state is not reflected in {@link ClientContext} objects created prior to
+ * changing the state.
+ * <p>
+ * The {@link ClientContext} returned is not available through the
+ * {@link #getCurrentClientContext()} method.
+ *
+ * @param resolver The {@code ResourceResolver} to base the
+ * {@link ClientContext} on.
+ * @return A newly created client context based on the given
+ * {@code ResourceResolver}.
+ * @throws IllegalArgumentException If {@code resolver} is {@code null}
*/
ClientContext createClientContext(ResourceResolver resolver);
/**
* Create a client context for the request.
- * @throws IllegalArgumentException If request is null
+ * <p>
+ * The {@link ClientContext} is a snapshot of the enablement state of the
+ * features at the time of creation. A change in the feature enablement
+ * state is not reflected in {@link ClientContext} objects created prior to
+ * changing the state.
+ * <p>
+ * The {@link ClientContext} returned is not available through the
+ * {@link #getCurrentClientContext()} method.
+ *
+ * @param request The {@code HttpServletRequest} to base the
+ * {@link ClientContext} on. If this is a
+ * {@code SlingHttpServletContext} the {@link ClientContext}'s
+ * resource resolver is set to the request's resource resolver.
+ * @return A newly created client context based on the given
+ * {@code HttpServletRequest}.
+ * @throws IllegalArgumentException If {@code request} is {@code null}
*/
- ClientContext createClientContext(SlingHttpServletRequest request);
+ ClientContext createClientContext(HttpServletRequest request);
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java Tue Jan 28 08:32:39 2014
@@ -21,15 +21,13 @@ package org.apache.sling.featureflags.im
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
@@ -38,28 +36,22 @@ public class ClientContextImpl implement
private final ExecutionContext featureContext;
- private final List<Feature> enabledFeatures;
+ private final Map<String, Feature> enabledFeatures;
- private final List<ResourceHiding> hidingFeatures;
+ private final List<ResourceDecorator> resourceDecorators;
- 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);
- }
- final ResourceTypeMapping rm = f.adaptTo(ResourceTypeMapping.class);
- if ( rm != null ) {
- final Map<String, String> mapping = rm.getResourceTypeMapping();
- mapperFeatures.putAll(mapping);
+ public ClientContextImpl(final ExecutionContext featureContext, final Map<String, Feature> features) {
+ ArrayList<ResourceDecorator> resourceDecorators = new ArrayList<ResourceDecorator>(features.size());
+ for (final Feature f : features.values()) {
+ if (f instanceof ResourceDecorator) {
+ resourceDecorators.add((ResourceDecorator) f);
}
}
- this.hidingFeatures = hiding;
+ resourceDecorators.trimToSize();
+
this.featureContext = featureContext;
+ this.enabledFeatures = Collections.unmodifiableMap(features);
+ this.resourceDecorators = Collections.unmodifiableList(resourceDecorators);
}
public ExecutionContext getFeatureContext() {
@@ -68,24 +60,15 @@ public class ClientContextImpl implement
@Override
public boolean isEnabled(final String featureName) {
- for(final Feature f : this.enabledFeatures) {
- if ( featureName.equals(f.getName()) ) {
- return true;
- }
- }
- return false;
+ return this.enabledFeatures.get(featureName) != null;
}
@Override
public Collection<Feature> getEnabledFeatures() {
- return this.enabledFeatures;
- }
-
- public Collection<ResourceHiding> getHidingFeatures() {
- return this.hidingFeatures;
+ return this.enabledFeatures.values();
}
- public Map<String, String> getResourceTypeMapping() {
- return this.mapperFeatures;
+ public List<ResourceDecorator> getResourceDecorators() {
+ return this.resourceDecorators;
}
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java Tue Jan 28 08:32:39 2014
@@ -26,45 +26,38 @@ import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
-
-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.SlingHttpServletRequest;
+import org.apache.sling.featureflags.ClientContext;
/**
- * This general servlet filter sets the current client context to the
- * current request.
+ * This general servlet filter sets the current client context to the current
+ * request.
*/
-@Component
-@Service(value=Filter.class)
-@Property(name="pattern", value="/.*")
public class CurrentClientContextFilter implements Filter {
- @Reference
- private FeatureManager manager;
+ private final FeatureManager featureManager;
+
+ public CurrentClientContextFilter(final FeatureManager featureManager) {
+ this.featureManager = featureManager;
+ }
@Override
- public void doFilter(final ServletRequest req, final ServletResponse res,
- final FilterChain chain)
- throws IOException, ServletException {
- if ( req instanceof SlingHttpServletRequest ) {
- manager.setCurrentClientContext((SlingHttpServletRequest)req);
- }
+ public void init(final FilterConfig config) {
+ // nothing to do
+ }
+
+ @Override
+ public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
+ throws IOException, ServletException {
+
+ ClientContext current = this.featureManager.setCurrentClientContext(req);
try {
chain.doFilter(req, res);
} finally {
- manager.unsetCurrentClientContext();
+ this.featureManager.unsetCurrentClientContext(current);
}
}
@Override
- public void init(final FilterConfig config) throws ServletException {
- // nothing to do
- }
-
- @Override
public void destroy() {
// nothing to do
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java Tue Jan 28 08:32:39 2014
@@ -18,6 +18,8 @@
*/
package org.apache.sling.featureflags.impl;
+import javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.featureflags.ExecutionContext;
@@ -29,20 +31,22 @@ public class ExecutionContextImpl implem
private final ResourceResolver resourceResolver;
- private final SlingHttpServletRequest request;
+ private final HttpServletRequest request;
public ExecutionContextImpl(final ResourceResolver resourceResolver) {
this.request = null;
this.resourceResolver = resourceResolver;
}
- public ExecutionContextImpl(final SlingHttpServletRequest request) {
+ public ExecutionContextImpl(final HttpServletRequest request) {
this.request = request;
- this.resourceResolver = request.getResourceResolver();
+ this.resourceResolver = (request instanceof SlingHttpServletRequest)
+ ? ((SlingHttpServletRequest) request).getResourceResolver()
+ : null;
}
@Override
- public SlingHttpServletRequest getRequest() {
+ public HttpServletRequest getRequest() {
return this.request;
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java Tue Jan 28 08:32:39 2014
@@ -22,51 +22,116 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Hashtable;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+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;
/**
- * This service implements the feature handling.
- * It keeps track of all {@link Feature} services.
+ * This service implements the feature handling. It keeps track of all
+ * {@link Feature} services.
*/
@Component
-@Reference(name="feature",
- cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE,
- policy=ReferencePolicy.DYNAMIC,
- referenceInterface=Feature.class)
-public class FeatureManager implements Features {
+@Reference(
+ name = "feature",
+ cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC,
+ referenceInterface = Feature.class)
+public class FeatureManager {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final ThreadLocal<ClientContext> perThreadClientContext = new ThreadLocal<ClientContext>();
+
+ private final ClientContext defaultClientContext = new ClientContext() {
+ @Override
+ public boolean isEnabled(final String featureName) {
+ return false;
+ }
+
+ @Override
+ public Collection<Feature> getEnabledFeatures() {
+ return Collections.emptyList();
+ }
+ };
+
private final Map<String, List<FeatureDescription>> allFeatures = new HashMap<String, List<FeatureDescription>>();
- private Map<String, FeatureDescription> activeFeatures = new TreeMap<String, FeatureDescription>();
+ private Map<String, Feature> activeFeatures = Collections.emptyMap();
- /**
- * Bind a new feature
- */
- protected void bindFeature(final Feature f, final Map<String, Object> props) {
- synchronized ( this.allFeatures ) {
+ private List<ServiceRegistration> services;
+
+ @SuppressWarnings("serial")
+ @Activate
+ private void activate(BundleContext bundleContext) {
+ ArrayList<ServiceRegistration> services = new ArrayList<ServiceRegistration>();
+ services.add(bundleContext.registerService(Features.class.getName(), new FeaturesImpl(this), null));
+ services.add(bundleContext.registerService(ResourceDecorator.class.getName(),
+ new FeatureResourceDecorator(this), null));
+ services.add(bundleContext.registerService(Servlet.class.getName(), new FeatureWebConsolePlugin(this),
+ new Hashtable<String, Object>() {
+ {
+ put("felix.webconsole.label", "features");
+ put("felix.webconsole.title", "Features");
+ put("felix.webconsole.category", "Sling");
+ }
+ }));
+ services.add(bundleContext.registerService(Filter.class.getName(), new CurrentClientContextFilter(this),
+ new Hashtable<String, Object>() {
+ {
+ put("sling.filter.scope", "REQUEST");
+ put("service.ranking", Integer.MIN_VALUE);
+ }
+ }));
+ this.services = services;
+ }
+
+ @Deactivate
+ private void deactivate() {
+ if (this.services != null) {
+ for (ServiceRegistration service : this.services) {
+ if (service != null) {
+ service.unregister();
+ }
+ }
+ this.services.clear();
+ this.services = null;
+ }
+ }
+
+ //--- Feature binding
+
+ // bind method for Feature services
+ @SuppressWarnings("unused")
+ private void bindFeature(final Feature f, final Map<String, Object> props) {
+ synchronized (this.allFeatures) {
final String name = f.getName();
final FeatureDescription info = new FeatureDescription(f, props);
List<FeatureDescription> candidates = this.allFeatures.get(name);
- if ( candidates == null ) {
+ if (candidates == null) {
candidates = new ArrayList<FeatureDescription>();
this.allFeatures.put(name, candidates);
}
@@ -77,18 +142,17 @@ public class FeatureManager implements F
}
}
- /**
- * Unbind a feature
- */
- protected void unbindFeature(final Feature f, final Map<String, Object> props) {
- synchronized ( this.allFeatures ) {
+ // unbind method for Feature services
+ @SuppressWarnings("unused")
+ private void unbindFeature(final Feature f, final Map<String, Object> props) {
+ synchronized (this.allFeatures) {
final String name = f.getName();
final FeatureDescription info = new FeatureDescription(f, props);
final List<FeatureDescription> candidates = this.allFeatures.get(name);
- if ( candidates != null ) { // sanity check
+ if (candidates != null) { // sanity check
candidates.remove(info);
- if ( candidates.size() == 0 ) {
+ if (candidates.size() == 0) {
this.allFeatures.remove(name);
}
}
@@ -96,56 +160,50 @@ public class FeatureManager implements F
}
}
+ // calculates map of active features (eliminating Feature name
+ // collisions). Must be called while synchronized on this.allFeatures
private void calculateActiveProviders() {
- final Map<String, FeatureDescription> activeMap = new TreeMap<String, FeatureDescription>();
- for(final Map.Entry<String, List<FeatureDescription>> entry : this.allFeatures.entrySet()) {
+ final Map<String, Feature> activeMap = new HashMap<String, Feature>();
+ for (final Map.Entry<String, List<FeatureDescription>> entry : this.allFeatures.entrySet()) {
final FeatureDescription desc = entry.getValue().get(0);
-
- activeMap.put(entry.getKey(), desc);
- if ( entry.getValue().size() > 1 ) {
+ activeMap.put(entry.getKey(), desc.feature);
+ if (entry.getValue().size() > 1) {
logger.warn("More than one feature service for feature {}", entry.getKey());
}
}
this.activeFeatures = activeMap;
}
- private final ThreadLocal<ClientContextImpl> perThreadClientContext = new ThreadLocal<ClientContextImpl>();
-
- private final ClientContext defaultClientContext = new ClientContext() {
+ //--- Client Context management and access
- @Override
- public boolean isEnabled(final String featureName) {
- return false;
- }
-
- @Override
- public Collection<Feature> getEnabledFeatures() {
- return Collections.emptyList();
- }
- };
-
- @Override
- public ClientContext getCurrentClientContext() {
+ ClientContext getCurrentClientContext() {
ClientContext result = perThreadClientContext.get();
- if ( result == null ) {
+ if (result == null) {
result = defaultClientContext;
}
return result;
}
- public void setCurrentClientContext(final SlingHttpServletRequest request) {
- final ExecutionContext providerContext = new ExecutionContextImpl(request);
- final ClientContextImpl ctx = this.createClientContext(providerContext);
- perThreadClientContext.set(ctx);
+ ClientContext setCurrentClientContext(final ServletRequest request) {
+ final ClientContext current = perThreadClientContext.get();
+ if (request instanceof HttpServletRequest) {
+ final ExecutionContext providerContext = new ExecutionContextImpl((HttpServletRequest) request);
+ final ClientContextImpl ctx = this.createClientContext(providerContext);
+ perThreadClientContext.set(ctx);
+ }
+ return current;
}
- public void unsetCurrentClientContext() {
- perThreadClientContext.remove();
+ void unsetCurrentClientContext(final ClientContext previous) {
+ if (previous != null) {
+ perThreadClientContext.set(previous);
+ } else {
+ perThreadClientContext.remove();
+ }
}
- @Override
- public ClientContext createClientContext(final ResourceResolver resolver) {
- if ( resolver == null ) {
+ ClientContext createClientContext(final ResourceResolver resolver) {
+ if (resolver == null) {
throw new IllegalArgumentException("Resolver must not be null.");
}
final ExecutionContext providerContext = new ExecutionContextImpl(resolver);
@@ -153,9 +211,8 @@ public class FeatureManager implements F
return ctx;
}
- @Override
- public ClientContext createClientContext(final SlingHttpServletRequest request) {
- if ( request == null ) {
+ ClientContext createClientContext(final HttpServletRequest request) {
+ if (request == null) {
throw new IllegalArgumentException("Request must not be null.");
}
final ExecutionContext providerContext = new ExecutionContextImpl(request);
@@ -164,75 +221,64 @@ public class FeatureManager implements F
}
private ClientContextImpl createClientContext(final ExecutionContext providerContext) {
- final List<Feature> enabledFeatures = new ArrayList<Feature>();
-
- for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
- final Feature f = entry.getValue().feature;
-
- if ( entry.getValue().feature.isEnabled(providerContext) ) {
- enabledFeatures.add(f);
+ final Map<String, Feature> enabledFeatures = new HashMap<String, Feature>();
+ for (final Map.Entry<String, Feature> entry : this.activeFeatures.entrySet()) {
+ if (entry.getValue().isEnabled(providerContext)) {
+ enabledFeatures.put(entry.getKey(), entry.getValue());
}
}
- final ClientContextImpl ctx = new ClientContextImpl(providerContext, enabledFeatures);
- return ctx;
+ return new ClientContextImpl(providerContext, enabledFeatures);
}
- @Override
- public Feature[] getAvailableFeatures() {
- final List<Feature> result = new ArrayList<Feature>();
- for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
- final Feature f = entry.getValue().feature;
- result.add(f);
- }
- return result.toArray(new Feature[result.size()]);
+ //--- Feature access
+
+ Feature[] getAvailableFeatures() {
+ final Map<String, Feature> activeFeatures = this.activeFeatures;
+ return activeFeatures.values().toArray(new Feature[activeFeatures.size()]);
}
- @Override
- public Feature getFeature(final String name) {
- final FeatureDescription desc = this.activeFeatures.get(name);
- if ( desc != null ) {
- return desc.feature;
- }
- return null;
+ Feature getFeature(final String name) {
+ return this.activeFeatures.get(name);
}
- @Override
- public String[] getAvailableFeatureNames() {
- return this.activeFeatures.keySet().toArray(new String[this.activeFeatures.size()]);
+ String[] getAvailableFeatureNames() {
+ final Map<String, Feature> activeFeatures = this.activeFeatures;
+ return activeFeatures.keySet().toArray(new String[activeFeatures.size()]);
}
- @Override
- public boolean isAvailable(final String featureName) {
+ boolean isAvailable(final String featureName) {
return this.activeFeatures.containsKey(featureName);
}
/**
- * Internal class caching some feature meta data like service id and ranking.
+ * Internal class caching some feature meta data like service id and
+ * ranking.
*/
private final static class FeatureDescription implements Comparable<FeatureDescription> {
public final int ranking;
+
public final long serviceId;
+
public final Feature feature;
- public FeatureDescription(final Feature feature,
- final Map<String, Object> props) {
+ public FeatureDescription(final Feature feature, final Map<String, Object> props) {
this.feature = feature;
final Object sr = props.get(Constants.SERVICE_RANKING);
- if ( sr == null || !(sr instanceof Integer)) {
- this.ranking = 0;
+ if (sr instanceof Integer) {
+ this.ranking = (Integer) sr;
} else {
- this.ranking = (Integer)sr;
+ this.ranking = 0;
}
- this.serviceId = (Long)props.get(Constants.SERVICE_ID);
+ this.serviceId = (Long) props.get(Constants.SERVICE_ID);
}
@Override
public int compareTo(final FeatureDescription o) {
- if ( this.ranking < o.ranking ) {
+ if (this.ranking < o.ranking) {
return 1;
- } else if (this.ranking > o.ranking ) {
+ } else if (this.ranking > o.ranking) {
return -1;
}
// If ranks are equal, then sort by service id in descending order.
@@ -241,8 +287,8 @@ public class FeatureManager implements F
@Override
public boolean equals(final Object obj) {
- if ( obj instanceof FeatureDescription ) {
- return ((FeatureDescription)obj).serviceId == this.serviceId;
+ if (obj instanceof FeatureDescription) {
+ return ((FeatureDescription) obj).serviceId == this.serviceId;
}
return false;
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java Tue Jan 28 08:32:39 2014
@@ -18,10 +18,8 @@
*/
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 javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.featureflags.ClientContext;
import org.apache.sling.featureflags.Feature;
@@ -30,12 +28,13 @@ import org.apache.sling.featureflags.Fea
/**
* 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() {
@@ -68,7 +67,7 @@ public class FeaturesImpl implements Fea
}
@Override
- public ClientContext createClientContext(final SlingHttpServletRequest request) {
+ public ClientContext createClientContext(final HttpServletRequest request) {
return this.manager.createClientContext(request);
}
}
Modified: sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java?rev=1561994&r1=1561993&r2=1561994&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java (original)
+++ sling/trunk/contrib/extensions/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java Tue Jan 28 08:32:39 2014
@@ -18,10 +18,48 @@
*/
/**
- * Provides a service to interface which may be implemented by applications
- * to get notified on cluster topology changes.
+ * The <i>Feature Flags</i> feature allows applications to dynamically
+ * provide features to clients and consumers depending on various criteria such as
+ * <ul>
+ * <li>Time of Day</li>
+ * <li>Static Configuration</li>
+ * <li>Request Parameter</li>
+ * <li>Some Resource</li>
+ * </ul>
+ * <p>
+ * Feature flag support consists of two parts: The feature flag itself represented
+ * by the {@link org.apache.sling.featureflags.Feature Feature} interface and the
+ * the application providing a feature guarded by a feature flag. Such applications
+ * make use of the {@link org.apache.sling.featureflags.Features Features} service to
+ * query feature flags.
+ * <p>
+ * Feature flags can be provided by registering
+ * {@link org.apache.sling.featureflags.Feature Feature} services. Alternatively
+ * feature flags can be provided by factory configuration with factory PID
+ * {@code org.apache.sling.featureflags.Feature} as follows:
+ * <table>
+ * <tr>
+ * <td>{@code name}</td>
+ * <td>Short name of this feature. This name is used to refer to this feature
+ * when checking for it to be enabled or not. This property is required
+ * and defaults to a name derived from the feature's class name and object
+ * identity. It is strongly recommended to define a useful and unique for the feature</td>
+ * </tr>
+ * <tr>
+ * <td>{@code description}</td>
+ * <td>Description for the feature. The intent is to descibe the behaviour
+ * of the application if this feature would be enabled. It is recommended
+ * to define this property. The default value is the value of the name property.</td>
+ * </tr>
+ * <tr>
+ * <td>{@code enabled}</td>
+ * <td>Boolean flag indicating whether the feature is enabled or not by
+ * this configuration</td>
+ * </tr>
+ * </table>
*
* @version 1.0
+ * @see <a href="http://sling.apache.org/documentation/the-sling-engine/featureflags.html">Feature Flags</a>
*/
@Version("1.0")
package org.apache.sling.featureflags;