This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.featureflags-1.0.0
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-featureflags.git

commit 7d315bae21cfaf139688360fbb1a400c964c0274
Author: Felix Meschberger <[email protected]>
AuthorDate: Thu Jan 30 08:00:45 2014 +0000

    SLING-3350 Simplify API and implementation:
    
    - Remove ClientContext interface (clients directly use Features service)
    - Add feature flag caching in the ExecutionContext
    - Improve API doc
    - Merge service implementations into FeatureManager
    - Remove ResourceDecorator (see issue for reasons)
    - Only one filter registration (extract ResourceResolver from request attr)
    
    git-svn-id: 
https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/feature-flags@1562712
 13f79535-47bb-0310-9956-ffa450edef68
---
 .../apache/sling/featureflags/ClientContext.java   |  53 -----
 .../sling/featureflags/ExecutionContext.java       |  15 +-
 .../org/apache/sling/featureflags/Feature.java     |  41 ++--
 .../org/apache/sling/featureflags/Features.java    |  84 ++------
 .../sling/featureflags/impl/ClientContextImpl.java |  74 -------
 .../impl/CurrentClientContextFilter.java           |  64 ------
 .../featureflags/impl/ExecutionContextImpl.java    |  45 ++--
 .../sling/featureflags/impl/FeatureManager.java    | 233 ++++++++++-----------
 .../impl/FeatureResourceDecorator.java             |  57 -----
 .../featureflags/impl/FeatureWebConsolePlugin.java |  74 -------
 .../sling/featureflags/impl/FeaturesImpl.java      |  73 -------
 .../apache/sling/featureflags/package-info.java    |   8 +-
 12 files changed, 184 insertions(+), 637 deletions(-)

diff --git a/src/main/java/org/apache/sling/featureflags/ClientContext.java 
b/src/main/java/org/apache/sling/featureflags/ClientContext.java
deleted file mode 100644
index 33a392c..0000000
--- a/src/main/java/org/apache/sling/featureflags/ClientContext.java
+++ /dev/null
@@ -1,53 +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.Collection;
-
-import aQute.bnd.annotation.ProviderType;
-
-/**
- * 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
- * implement it.
- */
-@ProviderType
-public interface ClientContext {
-
-    /**
-     * 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 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();
-}
diff --git a/src/main/java/org/apache/sling/featureflags/ExecutionContext.java 
b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
index 1e91088..321a092 100644
--- a/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
+++ b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
@@ -30,14 +30,14 @@ import aQute.bnd.annotation.ProviderType;
  * 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
  * behavior.
+ * <p>
+ * Instances of this interface are provided by the feature manager to the
+ * {@link Feature} services. This interface is not intended to be implemented 
by
+ * client and application code.
  */
 @ProviderType
 public interface ExecutionContext {
@@ -47,11 +47,8 @@ public interface ExecutionContext {
      * 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>
+     * @return the request or {@code null}
      */
     HttpServletRequest getRequest();
 
@@ -61,7 +58,7 @@ public interface ExecutionContext {
      * If a {@code ResourceResolver} object is not available in the context,
      * this method may return {@code null}.
      *
-     * @return the resource resolver
+     * @return the resource resolver or {@code null}
      */
     ResourceResolver getResourceResolver();
 }
diff --git a/src/main/java/org/apache/sling/featureflags/Feature.java 
b/src/main/java/org/apache/sling/featureflags/Feature.java
index 3984df5..1d835b9 100644
--- a/src/main/java/org/apache/sling/featureflags/Feature.java
+++ b/src/main/java/org/apache/sling/featureflags/Feature.java
@@ -22,31 +22,18 @@ import aQute.bnd.annotation.ConsumerType;
 
 /**
  * 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}.
+ * accessible through the {@link Features} service while those with lower
+ * service rankings are ignored.
+ * <p>
+ * This interface is expected to be implemented by feature providers.
  */
 @ConsumerType
 public interface Feature {
 
     /**
-     * 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
@@ -61,4 +48,24 @@ public interface Feature {
      *         {@code null} or an empty string.
      */
     String getDescription();
+
+    /**
+     * 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}.
+     * <p>
+     * This method is called by the {@link Feature} manager and is not intended
+     * to be called by application code directly.
+     *
+     * @param context The {@link ExecutionContext} providing a context to
+     *            evaluate whether the feature is enabled or not.
+     *            Implementations must not hold on to this context instance or
+     *            the values provided for longer than executing this method.
+     * @return {@code true} if this {@code Feature} is enabled in the given
+     *         {@link ExecutionContext}.
+     */
+    boolean isEnabled(ExecutionContext context);
 }
diff --git a/src/main/java/org/apache/sling/featureflags/Features.java 
b/src/main/java/org/apache/sling/featureflags/Features.java
index eb5faac..a1d2a69 100644
--- a/src/main/java/org/apache/sling/featureflags/Features.java
+++ b/src/main/java/org/apache/sling/featureflags/Features.java
@@ -18,45 +18,34 @@
  */
 package org.apache.sling.featureflags;
 
-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 {@code Features} service is the applications access point to the Feature
+ * Flag functionality. 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 (known) feature names.
+     * Get the list of all (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 names of the known features
+     * @return The known features
      */
-    String[] getAvailableFeatureNames();
+    Feature[] getFeatures();
 
     /**
-     * Get the list of all available (known) features.
+     * Returns the feature with the given name.
      * <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.
-     *
      * @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}.
@@ -64,62 +53,17 @@ public interface Features {
     Feature getFeature(String name);
 
     /**
-     * Checks whether a feature with the given name is available (known).
+     * Returns {@code true} if a feature with the given name is known and
+     * enabled under the current {@link ExecutionContext}.
      * <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.
-     *
-     * @return A client context.
-     */
-    ClientContext getCurrentClientContext();
-
-    /**
-     * Create a client context for the resource resolver.
-     * <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.
-     * <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}
+     * @param name The name of the feature to check for enablement.
+     * @return {@code true} if the named feature is known and enabled.
+     *         Specifically {@code false} is also returned if the named feature
+     *         is not known.
      */
-    ClientContext createClientContext(HttpServletRequest request);
+    boolean isEnabled(String name);
 }
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java 
b/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
deleted file mode 100644
index dc02b25..0000000
--- a/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
+++ /dev/null
@@ -1,74 +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 java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-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;
-
-/**
- * Implementation of the client context
- */
-public class ClientContextImpl implements ClientContext {
-
-    private final ExecutionContext featureContext;
-
-    private final Map<String, Feature> enabledFeatures;
-
-    private final List<ResourceDecorator> resourceDecorators;
-
-    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);
-            }
-        }
-        resourceDecorators.trimToSize();
-
-        this.featureContext = featureContext;
-        this.enabledFeatures = Collections.unmodifiableMap(features);
-        this.resourceDecorators = 
Collections.unmodifiableList(resourceDecorators);
-    }
-
-    public ExecutionContext getFeatureContext() {
-        return this.featureContext;
-    }
-
-    @Override
-    public boolean isEnabled(final String featureName) {
-        return this.enabledFeatures.get(featureName) != null;
-    }
-
-    @Override
-    public Collection<Feature> getEnabledFeatures() {
-        return this.enabledFeatures.values();
-    }
-
-    public List<ResourceDecorator> getResourceDecorators() {
-        return this.resourceDecorators;
-    }
-}
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
 
b/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
deleted file mode 100644
index da6287f..0000000
--- 
a/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
+++ /dev/null
@@ -1,64 +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 java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import org.apache.sling.featureflags.ClientContext;
-
-/**
- * This general servlet filter sets the current client context to the current
- * request.
- */
-public class CurrentClientContextFilter implements Filter {
-
-    private final FeatureManager featureManager;
-
-    public CurrentClientContextFilter(final FeatureManager featureManager) {
-        this.featureManager = featureManager;
-    }
-
-    @Override
-    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 {
-            this.featureManager.unsetCurrentClientContext(current);
-        }
-    }
-
-    @Override
-    public void destroy() {
-        // nothing to do
-    }
-}
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java 
b/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
index 82f82da..76d40c7 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
@@ -18,41 +18,40 @@
  */
 package org.apache.sling.featureflags.impl;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.auth.core.AuthenticationSupport;
 import org.apache.sling.featureflags.ExecutionContext;
+import org.apache.sling.featureflags.Feature;
 
 /**
  * Implementation of the provider context.
  */
 public class ExecutionContextImpl implements ExecutionContext {
 
+    private static final String REQUEST_ATTRIBUTE_RESOLVER = 
"org.apache.sling.auth.core.ResourceResolver";
+
     private final ResourceResolver resourceResolver;
 
     private final HttpServletRequest request;
 
-    public ExecutionContextImpl(final ResourceResolver resourceResolver) {
-        this.request = null;
-        this.resourceResolver = resourceResolver;
-    }
+    private final Map<String, Boolean> featureCache;
 
     public ExecutionContextImpl(final HttpServletRequest request) {
-        this.request = request;
-        ResourceResolver resolver = (request instanceof 
SlingHttpServletRequest)
-                ? ((SlingHttpServletRequest) request).getResourceResolver()
-                : null;
-        if ( resolver == null ) {
-            // get ResourceResolver (set by AuthenticationSupport)
-            final Object resolverObject = 
request.getAttribute(AuthenticationSupport.REQUEST_ATTRIBUTE_RESOLVER);
-            resolver = (resolverObject instanceof ResourceResolver)
-                    ? (ResourceResolver) resolverObject
-                    : null;
-
+        ResourceResolver resourceResolver = null;
+        if (request != null) {
+            Object resolverObject = 
request.getAttribute(REQUEST_ATTRIBUTE_RESOLVER);
+            if (resolverObject instanceof ResourceResolver) {
+                resourceResolver = (ResourceResolver) resolverObject;
+            }
         }
-        this.resourceResolver = resolver;
+
+        this.request = request;
+        this.resourceResolver = resourceResolver;
+        this.featureCache = new HashMap<String, Boolean>();
     }
 
     @Override
@@ -64,4 +63,14 @@ public class ExecutionContextImpl implements 
ExecutionContext {
     public ResourceResolver getResourceResolver() {
         return this.resourceResolver;
     }
+
+    boolean isEnabled(Feature feature) {
+        final String name = feature.getName();
+        Boolean entry = this.featureCache.get(name);
+        if (entry == null) {
+            entry = Boolean.valueOf(feature.isEnabled(this));
+            this.featureCache.put(name, entry);
+        }
+        return entry;
+    }
 }
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java 
b/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
index b5297a8..c4f6b84 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
@@ -18,34 +18,36 @@
  */
 package org.apache.sling.featureflags.impl;
 
+import java.io.IOException;
+import java.io.PrintWriter;
 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 javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
 import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
-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.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
 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.resource.ResourceDecorator;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.featureflags.ClientContext;
-import org.apache.sling.featureflags.ExecutionContext;
+import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.featureflags.Feature;
 import org.apache.sling.featureflags.Features;
-import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceRegistration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,71 +55,114 @@ import org.slf4j.LoggerFactory;
  * This service implements the feature handling. It keeps track of all
  * {@link Feature} services.
  */
-@Component
+@Component(policy = ConfigurationPolicy.IGNORE)
+@Service
 @Reference(
         name = "feature",
         cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
         policy = ReferencePolicy.DYNAMIC,
         referenceInterface = Feature.class)
-public class FeatureManager {
+@Properties({
+    @Property(name = "felix.webconsole.label", value = "features"),
+    @Property(name = "felix.webconsole.title", value = "Features"),
+    @Property(name = "felix.webconsole.category", value = "Sling"),
+    @Property(name = "pattern", value = "/.*"),
+    @Property(name = "service.ranking", intValue = 0x4000)
+})
+public class FeatureManager implements Features, Filter, Servlet {
 
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    private final ThreadLocal<ClientContext> perThreadClientContext = new 
ThreadLocal<ClientContext>();
+    private final ThreadLocal<ExecutionContextImpl> perThreadClientContext = 
new ThreadLocal<ExecutionContextImpl>();
 
-    private final ClientContext defaultClientContext = new ClientContext() {
-        @Override
-        public boolean isEnabled(final String featureName) {
-            return false;
+    private final Map<String, List<FeatureDescription>> allFeatures = new 
HashMap<String, List<FeatureDescription>>();
+
+    private Map<String, Feature> activeFeatures = Collections.emptyMap();
+
+    private ServletConfig servletConfig;
+
+    //--- Features
+
+    public Feature[] getFeatures() {
+        final Map<String, Feature> activeFeatures = this.activeFeatures;
+        return activeFeatures.values().toArray(new 
Feature[activeFeatures.size()]);
+    }
+
+    public Feature getFeature(final String name) {
+        return this.activeFeatures.get(name);
+    }
+
+    public boolean isEnabled(String featureName) {
+        final Feature feature = this.getFeature(featureName);
+        if (feature != null) {
+            return getCurrentExecutionContext().isEnabled(feature);
         }
+        return false;
+    }
 
-        @Override
-        public Collection<Feature> getEnabledFeatures() {
-            return Collections.emptyList();
+    //--- Filter
+
+    @Override
+    public void init(FilterConfig filterConfig) {
+        // nothing todo do
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain) throws IOException,
+            ServletException {
+        try {
+            this.pushContext((HttpServletRequest) request);
+            chain.doFilter(request, response);
+        } finally {
+            this.popContext();
         }
-    };
+    }
 
-    private final Map<String, List<FeatureDescription>> allFeatures = new 
HashMap<String, List<FeatureDescription>>();
+    @Override
+    public void destroy() {
+        // method shared by Servlet and Filter interface
+        this.servletConfig = null;
+    }
 
-    private Map<String, Feature> activeFeatures = Collections.emptyMap();
+    //--- Servlet
 
-    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("pattern", "/.*");
-                    put("service.ranking", Integer.MIN_VALUE);
-                }
-            }));
-        this.services = services;
+    @Override
+    public void init(ServletConfig config) {
+        this.servletConfig = config;
+    }
+
+    @Override
+    public ServletConfig getServletConfig() {
+        return this.servletConfig;
+    }
+
+    @Override
+    public String getServletInfo() {
+        return "Features";
     }
 
-    @Deactivate
-    private void deactivate() {
-        if (this.services != null) {
-            for (ServiceRegistration service : this.services) {
-                if (service != null) {
-                    service.unregister();
+    @Override
+    public void service(ServletRequest req, ServletResponse res) throws 
IOException {
+        if ("GET".equals(((HttpServletRequest) req).getMethod())) {
+            final PrintWriter pw = res.getWriter();
+            final Feature[] features = getFeatures();
+            if (features == null || features.length == 0) {
+                pw.println("<p class='statline ui-state-highlight'>No Features 
currently defined</p>");
+            } else {
+                pw.printf("<p class='statline ui-state-highlight'>%d 
Feature(s) currently defined</p>%n",
+                    features.length);
+                pw.println("<table class='nicetable'>");
+                
pw.println("<tr><th>Name</th><th>Description</th><th>Enabled</th></tr>");
+                final ExecutionContextImpl ctx = getCurrentExecutionContext();
+                for (final Feature feature : features) {
+                    pw.printf("<tr><td>%s</td><td>%s</td><td>%s</td></tr>%n", 
feature.getName(),
+                        feature.getDescription(), ctx.isEnabled(feature));
                 }
+                pw.println("</table>");
             }
-            this.services.clear();
-            this.services = null;
+        } else {
+            ((HttpServletResponse) 
res).sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
+            res.flushBuffer();
         }
     }
 
@@ -176,79 +221,17 @@ public class FeatureManager {
 
     //--- Client Context management and access
 
-    ClientContext getCurrentClientContext() {
-        ClientContext result = perThreadClientContext.get();
-        if (result == null) {
-            result = defaultClientContext;
-        }
-        return result;
-    }
-
-    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;
-    }
-
-    void unsetCurrentClientContext(final ClientContext previous) {
-        if (previous != null) {
-            perThreadClientContext.set(previous);
-        } else {
-            perThreadClientContext.remove();
-        }
-    }
-
-    ClientContext createClientContext(final ResourceResolver resolver) {
-        if (resolver == null) {
-            throw new IllegalArgumentException("Resolver must not be null.");
-        }
-        final ExecutionContext providerContext = new 
ExecutionContextImpl(resolver);
-        final ClientContext ctx = this.createClientContext(providerContext);
-        return ctx;
-    }
-
-    ClientContext createClientContext(final HttpServletRequest request) {
-        if (request == null) {
-            throw new IllegalArgumentException("Request must not be null.");
-        }
-        final ExecutionContext providerContext = new 
ExecutionContextImpl(request);
-        final ClientContext ctx = this.createClientContext(providerContext);
-        return ctx;
-    }
-
-    private ClientContextImpl createClientContext(final ExecutionContext 
providerContext) {
-        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());
-            }
-        }
-
-        return new ClientContextImpl(providerContext, enabledFeatures);
+    void pushContext(final HttpServletRequest request) {
+        this.perThreadClientContext.set(new ExecutionContextImpl(request));
     }
 
-    //--- Feature access
-
-    Feature[] getAvailableFeatures() {
-        final Map<String, Feature> activeFeatures = this.activeFeatures;
-        return activeFeatures.values().toArray(new 
Feature[activeFeatures.size()]);
-    }
-
-    Feature getFeature(final String name) {
-        return this.activeFeatures.get(name);
-    }
-
-    String[] getAvailableFeatureNames() {
-        final Map<String, Feature> activeFeatures = this.activeFeatures;
-        return activeFeatures.keySet().toArray(new 
String[activeFeatures.size()]);
+    void popContext() {
+        this.perThreadClientContext.set(null);
     }
 
-    boolean isAvailable(final String featureName) {
-        return this.activeFeatures.containsKey(featureName);
+    ExecutionContextImpl getCurrentExecutionContext() {
+        ExecutionContextImpl ctx = this.perThreadClientContext.get();
+        return (ctx != null) ? ctx : new ExecutionContextImpl(null);
     }
 
     /**
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
 
b/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
deleted file mode 100644
index 4985ab0..0000000
--- 
a/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
+++ /dev/null
@@ -1,57 +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.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);
-    }
-}
diff --git 
a/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java 
b/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
deleted file mode 100644
index ff2b8d7..0000000
--- 
a/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
+++ /dev/null
@@ -1,74 +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 java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.featureflags.ExecutionContext;
-import org.apache.sling.featureflags.Feature;
-
-@SuppressWarnings("serial")
-public class FeatureWebConsolePlugin extends HttpServlet {
-
-    private final FeatureManager featureManager;
-
-    FeatureWebConsolePlugin(final FeatureManager featureManager) {
-        this.featureManager = featureManager;
-    }
-
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
throws IOException {
-        final PrintWriter pw = resp.getWriter();
-        final Feature[] features = this.featureManager.getAvailableFeatures();
-        if (features == null || features.length == 0) {
-            pw.println("<p class='statline ui-state-highlight'>No Features 
currently defined</p>");
-        } else {
-            pw.printf("<p class='statline ui-state-highlight'>%d Feature(s) 
currently defined</p>%n", features.length);
-            pw.println("<table class='nicetable'>");
-            
pw.println("<tr><th>Name</th><th>Description</th><th>Enabled</th></tr>");
-            final ExecutionContext ctx = createContext(req);
-            for (final Feature feature : features) {
-                pw.printf("<tr><td>%s</td><td>%s</td><td>%s</td></tr>%n", 
feature.getName(), feature.getDescription(),
-                    feature.isEnabled(ctx));
-            }
-            pw.println("</table>");
-        }
-    }
-
-    private ExecutionContext createContext(final HttpServletRequest req) {
-        return new ExecutionContext() {
-
-            @Override
-            public ResourceResolver getResourceResolver() {
-                return null;
-            }
-
-            @Override
-            public HttpServletRequest getRequest() {
-                return req;
-            }
-        };
-    }
-}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java 
b/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
deleted file mode 100644
index eb67dd6..0000000
--- a/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
+++ /dev/null
@@ -1,73 +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.sling.api.resource.ResourceResolver;
-import org.apache.sling.featureflags.ClientContext;
-import org.apache.sling.featureflags.Feature;
-import org.apache.sling.featureflags.Features;
-
-/**
- * This is a wrapper around the internal feature manager.
- */
-public class FeaturesImpl implements Features {
-
-    private final FeatureManager manager;
-
-    FeaturesImpl(final FeatureManager manager) {
-        this.manager = manager;
-    }
-
-    @Override
-    public String[] getAvailableFeatureNames() {
-        return this.manager.getAvailableFeatureNames();
-    }
-
-    @Override
-    public boolean isAvailable(final String featureName) {
-        return this.manager.isAvailable(featureName);
-    }
-
-    @Override
-    public Feature[] getAvailableFeatures() {
-        return this.manager.getAvailableFeatures();
-    }
-
-    @Override
-    public Feature getFeature(final String name) {
-        return this.manager.getFeature(name);
-    }
-
-    @Override
-    public ClientContext getCurrentClientContext() {
-        return this.manager.getCurrentClientContext();
-    }
-
-    @Override
-    public ClientContext createClientContext(final ResourceResolver resolver) {
-        return this.manager.createClientContext(resolver);
-    }
-
-    @Override
-    public ClientContext createClientContext(final HttpServletRequest request) 
{
-        return this.manager.createClientContext(request);
-    }
-}
diff --git a/src/main/java/org/apache/sling/featureflags/package-info.java 
b/src/main/java/org/apache/sling/featureflags/package-info.java
index 41b243d..c1803b5 100644
--- a/src/main/java/org/apache/sling/featureflags/package-info.java
+++ b/src/main/java/org/apache/sling/featureflags/package-info.java
@@ -40,16 +40,18 @@
  * <table>
  *  <tr>
  *      <td>{@code name}</td>
- *      <td>Short name of this feature. This name is used to refer to this 
feature
+ *      <td>Short name of the feature. This name is used to refer to the 
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>
+ *          identity. It is strongly recommended to define a useful and unique 
name
+ *          for the feature</td>
  *  </tr>
  *  <tr>
  *      <td>{@code description}</td>
  *      <td>Description for the feature. The intent is to describe the behavior
  *          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>
+ *          to define this property. The default value is the name of the 
feature
+ *          as derived from the {@code name} property.</td>
  *  </tr>
  *  <tr>
  *      <td>{@code enabled}</td>

-- 
To stop receiving notification emails like this one, please contact
"[email protected]" <[email protected]>.

Reply via email to