You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:34:33 UTC
[sling-org-apache-sling-featureflags] 14/25: SLING-3148 Merge back
from whiteboard/fmeschbe/featureflags/feature-flags
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 5e66975857d4ad36946668ae7da2cb9e125acdbb
Author: Felix Meschberger <fm...@apache.org>
AuthorDate: Tue Jan 28 08:32:39 2014 +0000
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
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/feature-flags@1561994 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 10 +-
.../apache/sling/featureflags/ClientContext.java | 22 +-
.../sling/featureflags/ExecutionContext.java | 35 ++-
.../org/apache/sling/featureflags/Feature.java | 38 ++--
.../org/apache/sling/featureflags/Features.java | 81 +++++--
.../apache/sling/featureflags/ResourceHiding.java | 41 ----
.../sling/featureflags/ResourceTypeMapping.java | 41 ----
.../sling/featureflags/impl/ClientContextImpl.java | 49 ++---
.../sling/featureflags/impl/ConfiguredFeature.java | 105 +++++++++
.../impl/CurrentClientContextFilter.java | 43 ++--
.../featureflags/impl/ExecutionContextImpl.java | 12 +-
.../sling/featureflags/impl/FeatureManager.java | 242 ++++++++++++---------
...atorImpl.java => FeatureResourceDecorator.java} | 40 ++--
.../featureflags/impl/FeatureWebConsolePlugin.java | 74 +++++++
.../sling/featureflags/impl/FeaturesImpl.java | 17 +-
.../featureflags/impl/ResourceAccessImpl.java | 58 -----
.../apache/sling/featureflags/package-info.java | 42 +++-
17 files changed, 563 insertions(+), 387 deletions(-)
diff --git a/pom.xml b/pom.xml
index 7f62fee..75a28cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -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>
diff --git a/src/main/java/org/apache/sling/featureflags/ClientContext.java b/src/main/java/org/apache/sling/featureflags/ClientContext.java
index 61d5814..137e6ff 100644
--- a/src/main/java/org/apache/sling/featureflags/ClientContext.java
+++ b/src/main/java/org/apache/sling/featureflags/ClientContext.java
@@ -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();
}
diff --git a/src/main/java/org/apache/sling/featureflags/ExecutionContext.java b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
index d01bd67..14d684d 100644
--- a/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
+++ b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
@@ -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();
diff --git a/src/main/java/org/apache/sling/featureflags/Feature.java b/src/main/java/org/apache/sling/featureflags/Feature.java
index 5bec8fd..3984df5 100644
--- a/src/main/java/org/apache/sling/featureflags/Feature.java
+++ b/src/main/java/org/apache/sling/featureflags/Feature.java
@@ -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();
}
diff --git a/src/main/java/org/apache/sling/featureflags/Features.java b/src/main/java/org/apache/sling/featureflags/Features.java
index 8b6a810..eb5faac 100644
--- a/src/main/java/org/apache/sling/featureflags/Features.java
+++ b/src/main/java/org/apache/sling/featureflags/Features.java
@@ -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);
}
diff --git a/src/main/java/org/apache/sling/featureflags/ResourceHiding.java b/src/main/java/org/apache/sling/featureflags/ResourceHiding.java
deleted file mode 100644
index d19fab6..0000000
--- a/src/main/java/org/apache/sling/featureflags/ResourceHiding.java
+++ /dev/null
@@ -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);
-}
diff --git a/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java b/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java
deleted file mode 100644
index a9ca5f4..0000000
--- a/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java
+++ /dev/null
@@ -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();
-}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java b/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
index b71ebe4..dc02b25 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java
@@ -21,15 +21,13 @@ package org.apache.sling.featureflags.impl;
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 implements ClientContext {
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 implements ClientContext {
@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;
}
}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/ConfiguredFeature.java b/src/main/java/org/apache/sling/featureflags/impl/ConfiguredFeature.java
new file mode 100644
index 0000000..1f4db86
--- /dev/null
+++ b/src/main/java/org/apache/sling/featureflags/impl/ConfiguredFeature.java
@@ -0,0 +1,105 @@
+/*
+ * 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.Map;
+
+import javax.servlet.ServletRequest;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.featureflags.ExecutionContext;
+import org.apache.sling.featureflags.Feature;
+import org.osgi.framework.Constants;
+
+@Component(
+ name = "org.apache.sling.featureflags.Feature",
+ metatype = true,
+ configurationFactory = true,
+ policy = ConfigurationPolicy.REQUIRE,
+ label = "Statically Configured Feature",
+ description = "Allows for the definition of statically configured features which are defined and enabled through OSGi configuration")
+@Service
+public class ConfiguredFeature implements Feature {
+
+ private static final String PROP_FEATURE = "feature";
+
+ @Property(label = "Name", description = "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")
+ private static final String NAME = "name";
+
+ @Property(label = "Description", description = "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.")
+ private static final String DESCRIPTION = "description";
+
+ @Property(boolValue = false, label = "Enabled", description = "Boolean flag indicating whether the feature is "
+ + "enabled or not by this configuration")
+ private static final String ENABLED = "enabled";
+
+ private String name;
+
+ private String description;
+
+ private boolean enabled;
+
+ @Activate
+ private void activate(final Map<String, Object> configuration) {
+ final String pid = PropertiesUtil.toString(configuration.get(Constants.SERVICE_PID), getClass().getName() + "$"
+ + System.identityHashCode(this));
+ this.name = PropertiesUtil.toString(configuration.get(NAME), pid);
+ this.description = PropertiesUtil.toString(configuration.get(DESCRIPTION), this.name);
+ this.enabled = PropertiesUtil.toBoolean(configuration.get(ENABLED), false);
+ }
+
+ @Override
+ public boolean isEnabled(ExecutionContext context) {
+
+ // Request Parameter Override
+ ServletRequest request = context.getRequest();
+ if (request != null) {
+ String[] features = request.getParameterValues(PROP_FEATURE);
+ if (features != null) {
+ for (String feature : features) {
+ if (this.name.equals(feature)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return this.enabled;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getDescription() {
+ return this.description;
+ }
+}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java b/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
index 9399666..da6287f 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/CurrentClientContextFilter.java
@@ -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
}
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 555575f..cfd673a 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
@@ -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 implements ExecutionContext {
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;
}
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 fd601f6..61b5641 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
@@ -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();
+
+ 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;
+ }
- /**
- * Bind a new feature
- */
- protected void bindFeature(final Feature f, final Map<String, Object> props) {
- synchronized ( this.allFeatures ) {
+ @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 Features {
}
}
- /**
- * 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 Features {
}
}
+ // 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() {
-
- @Override
- public boolean isEnabled(final String featureName) {
- return false;
- }
-
- @Override
- public Collection<Feature> getEnabledFeatures() {
- return Collections.emptyList();
- }
- };
+ //--- Client Context management and access
- @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 Features {
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 Features {
}
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 Features {
@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;
}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java b/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
similarity index 58%
rename from src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java
rename to src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
index e2f818d..4985ab0 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/ResourceDecoratorImpl.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/FeatureResourceDecorator.java
@@ -20,46 +20,34 @@ 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 {
+public class FeatureResourceDecorator implements ResourceDecorator {
- @Reference
- private FeatureManager manager;
+ 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 != 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;
- }
- };
+ if (info instanceof ClientContextImpl) {
+ for (ResourceDecorator rd : ((ClientContextImpl) info).getResourceDecorators()) {
+ Resource r = rd.decorate(resource);
+ if (r != null) {
+ result = r;
+ }
}
}
- return resource;
+ return result;
}
@Override
diff --git a/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java b/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
new file mode 100644
index 0000000..ff2b8d7
--- /dev/null
+++ b/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
@@ -0,0 +1,74 @@
+/*
+ * 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
index e0e836e..eb67dd6 100644
--- a/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
+++ b/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
@@ -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.Features;
/**
* 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 Features {
}
@Override
- public ClientContext createClientContext(final SlingHttpServletRequest request) {
+ public ClientContext createClientContext(final HttpServletRequest request) {
return this.manager.createClientContext(request);
}
}
diff --git a/src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java b/src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java
deleted file mode 100644
index 8772cf3..0000000
--- a/src/main/java/org/apache/sling/featureflags/impl/ResourceAccessImpl.java
+++ /dev/null
@@ -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);
- }
-}
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 90264d0..f1fb45b 100644
--- a/src/main/java/org/apache/sling/featureflags/package-info.java
+++ b/src/main/java/org/apache/sling/featureflags/package-info.java
@@ -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;
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.