You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2014/01/27 09:58:11 UTC
svn commit: r1561613 - in
/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags:
./ impl/
Author: fmeschbe
Date: Mon Jan 27 08:58:11 2014
New Revision: 1561613
URL: http://svn.apache.org/r1561613
Log:
Multiple Enhancements:
* Cleanup the API
* Base ClientContext and ExecutionContext on HttpServletReequest
* Add(/improve JavaDoc
* Add WebConsolePlugin
* Add (factory) configuration support to define features
Added:
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java (with props)
Modified:
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ClientContext.java Mon Jan 27 08:58:11 2014
@@ -23,9 +23,12 @@ 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 {
@@ -41,8 +44,10 @@ public interface ClientContext {
boolean isEnabled(String featureName);
/**
- * Returns a list of all enabled features
- * @return The list of features, the list might be empty.
+ * Returns a possibly empty collection of enabled {@link Feature} instances.
+ *
+ * @return The collection of enabled {@link Feature} instances. This
+ * collection may be empty and is not modifiable.
*/
Collection<Feature> getEnabledFeatures();
}
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/ExecutionContext.java Mon Jan 27 08:58:11 2014
@@ -18,26 +18,49 @@
*/
package org.apache.sling.featureflags;
-import org.apache.sling.api.SlingHttpServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.resource.ResourceResolver;
import aQute.bnd.annotation.ProviderType;
/**
- * The provider context contains all information that is passed to a
- * {@link Feature} in order to check whether a feature is enabled.
+ * The {@code ExecutionContext} interface provides access to the context for
+ * evaluating whether a feature is enabled or not. Instances of this object are
+ * provided to the {@link Feature#isEnabled(ExecutionContext)} to help
+ * evaluating whether the feature is enabled or not.
+ * <p>
+ * The {@link Features} service {@link ClientContext} generating methods create
+ * an instance of this to collect the enabled {@link Feature} services for the
+ * creation of the {@link ClientContext} instance.
+ * <p>
+ * This object provides access to live data and must only be used to read
+ * information. Modifying content through a {@code ResourceResolver} directly or
+ * indirectly provided by this object is considered inappropriate and faulty
+ * behaviour.
*/
@ProviderType
public interface ExecutionContext {
/**
- * Return the associated request if available
+ * Returns a {@code HttpServletRequest} object to retrieve information which
+ * may influence the decision whether a {@link Feature} is enabled or not.
+ * If a {@code HttpServletRequest} object is not available in the context,
+ * this method may return {@code null}.
+ * <p>
+ * In a Sling request processing context, the {@code HttpServletRequest}
+ * object returned may actually be a {@code SlingHttpServletRequest}.
+ *
* @return the request or <code>null</code>
*/
- SlingHttpServletRequest getRequest();
+ HttpServletRequest getRequest();
/**
- * Return the associated resource resolver.
+ * Returns a {@code ResourceResolver} object to retrieve information which
+ * may influence the decision whether a {@link Feature} is enabled or not.
+ * If a {@code ResourceResolver} object is not available in the context,
+ * this method may return {@code null}.
+ *
* @return the resource resolver
*/
ResourceResolver getResourceResolver();
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Feature.java Mon Jan 27 08:58:11 2014
@@ -21,26 +21,44 @@ package org.apache.sling.featureflags;
import aQute.bnd.annotation.ConsumerType;
/**
- * A feature is defined by its name.
- *
- * 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 {
/**
- * Checks whether the feature is enabled for the current execution
- * context.
+ * Checks whether the feature is enabled for the given execution context.
+ * <p>
+ * Multiple calls to this method may but are not required to return the same
+ * value. For example the return value may depend on the time of day, some
+ * random number or some information provided by the given
+ * {@link ExecutionContext}.
+ *
+ * @param context The {@link ExecutionContext} providing a context to
+ * evaluate whether the feature is enabled or not.
+ * @return {@code true} if this {@code Feature} is enabled in the given
+ * {@link ExecutionContext}.
*/
boolean isEnabled(ExecutionContext context);
/**
* The name of the feature.
+ *
+ * @return The name of this feature which must not be {@code null} or an
+ * empty string.
*/
String getName();
/**
* The description of the feature.
+ *
+ * @return The optional description of this feature, which may be
+ * {@code null} or an empty string.
*/
String getDescription();
}
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/Features.java Mon Jan 27 08:58:11 2014
@@ -18,69 +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 {
/**
- * Resource property of type String or String[] listing the features
- * applicable to a resource.
+ * Get the list of all available (known) feature names.
* <p>
- * If the ResourceResolver supports feature flags, the respective resource
- * property is expected to be copied into the ResourceMetadata as a String[]
- * property of this name.
- */
- String FEATURE_PROPERTY = "sling:features";
-
- /**
- * Get the list of all available feature names. A feature is available
- * if there is a registered {@link Feature} service.
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @return The names of the known features
*/
String[] getAvailableFeatureNames();
/**
- * Get the list of all available features. A feature is available
- * if there is a registered {@link Feature} service.
+ * Get the list of all available (known) features.
+ * <p>
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @return The known features
*/
Feature[] getAvailableFeatures();
/**
* Returns the feature with the given name.
- * @return The feature or <code>null</code>
+ *
+ * @param name The name of the feature.
+ * @return The feature or <code>null</code> if not known or the name is an
+ * empty string or {@code null}.
*/
Feature getFeature(String name);
/**
- * Checks whether a feature with the given name is available.
- * A feature is available if there is a registered {@link Feature} service.
+ * Checks whether a feature with the given name is available (known).
+ * <p>
+ * Features are known if they are registered as {@link Feature} services or
+ * are configured with OSGi configuration whose factory PID is
+ * {@code org.apache.sling.featureflags.Feature}.
+ *
+ * @param featureName The name of the feature to check for availability.
+ * @return {@code true} if the named feature is available.
*/
boolean isAvailable(String featureName);
/**
- * Returns the current client context.
- * This method always returns a client context object
+ * Returns the current client context. This method always returns a client
+ * context object.
+ *
* @return A client context.
*/
ClientContext getCurrentClientContext();
/**
* Create a client context for the resource resolver.
- * @throws IllegalArgumentException If resolver is null
+ * <p>
+ * The {@link ClientContext} is a snapshot of the enablement state of the
+ * features at the time of creation. A change in the feature enablement
+ * state is not reflected in {@link ClientContext} objects created prior to
+ * changing the state.
+ * <p>
+ * The {@link ClientContext} returned is not available through the
+ * {@link #getCurrentClientContext()} method.
+ *
+ * @param resolver The {@code ResourceResolver} to base the
+ * {@link ClientContext} on.
+ * @return A newly created client context based on the given
+ * {@code ResourceResolver}.
+ * @throws IllegalArgumentException If {@code resolver} is {@code null}
*/
ClientContext createClientContext(ResourceResolver resolver);
/**
* Create a client context for the request.
- * @throws IllegalArgumentException If request is null
+ * <p>
+ * The {@link ClientContext} is a snapshot of the enablement state of the
+ * features at the time of creation. A change in the feature enablement
+ * state is not reflected in {@link ClientContext} objects created prior to
+ * changing the state.
+ * <p>
+ * The {@link ClientContext} returned is not available through the
+ * {@link #getCurrentClientContext()} method.
+ *
+ * @param request The {@code HttpServletRequest} to base the
+ * {@link ClientContext} on. If this is a
+ * {@code SlingHttpServletContext} the {@link ClientContext}'s
+ * resource resolver is set to the request's resource resolver.
+ * @return A newly created client context based on the given
+ * {@code HttpServletRequest}.
+ * @throws IllegalArgumentException If {@code request} is {@code null}
*/
- ClientContext createClientContext(SlingHttpServletRequest request);
+ ClientContext createClientContext(HttpServletRequest request);
}
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java Mon Jan 27 08:58:11 2014
@@ -18,6 +18,8 @@
*/
package org.apache.sling.featureflags.impl;
+import javax.servlet.http.HttpServletRequest;
+
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.featureflags.ExecutionContext;
@@ -29,20 +31,22 @@ public class ExecutionContextImpl implem
private final ResourceResolver resourceResolver;
- private final SlingHttpServletRequest request;
+ private final HttpServletRequest request;
public ExecutionContextImpl(final ResourceResolver resourceResolver) {
this.request = null;
this.resourceResolver = resourceResolver;
}
- public ExecutionContextImpl(final SlingHttpServletRequest request) {
+ public ExecutionContextImpl(final HttpServletRequest request) {
this.request = request;
- this.resourceResolver = request.getResourceResolver();
+ this.resourceResolver = (request instanceof SlingHttpServletRequest)
+ ? ((SlingHttpServletRequest) request).getResourceResolver()
+ : null;
}
@Override
- public SlingHttpServletRequest getRequest() {
+ public HttpServletRequest getRequest() {
return this.request;
}
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java Mon Jan 27 08:58:11 2014
@@ -22,17 +22,20 @@ 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.Servlet;
+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;
@@ -46,14 +49,15 @@ 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)
+@Reference(
+ name = "feature",
+ cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
+ policy = ReferencePolicy.DYNAMIC,
+ referenceInterface = Feature.class)
public class FeatureManager {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -66,15 +70,31 @@ public class FeatureManager {
private ServiceRegistration resourceDecorator;
+ private ServiceRegistration featureWebConsolePlugin;
+
+ @SuppressWarnings("serial")
@Activate
private void activate(BundleContext bundleContext) {
this.featuresService = bundleContext.registerService(Features.class.getName(), new FeaturesImpl(this), null);
this.resourceDecorator = bundleContext.registerService(ResourceDecorator.class.getName(),
new FeatureResourceDecorator(this), null);
+ this.featureWebConsolePlugin = 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");
+ }
+ });
}
@Deactivate
private void deactivate() {
+ if (this.featureWebConsolePlugin != null) {
+ this.featureWebConsolePlugin.unregister();
+ this.featureWebConsolePlugin = null;
+ }
+
if (this.featuresService != null) {
this.featuresService.unregister();
this.featuresService = null;
@@ -86,16 +106,13 @@ public class FeatureManager {
}
}
- /**
- * Bind a new feature
- */
protected void bindFeature(final Feature f, final Map<String, Object> props) {
- synchronized ( this.allFeatures ) {
+ 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);
}
@@ -106,18 +123,15 @@ public class FeatureManager {
}
}
- /**
- * Unbind a feature
- */
protected void unbindFeature(final Feature f, final Map<String, Object> props) {
- synchronized ( this.allFeatures ) {
+ 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);
}
}
@@ -127,11 +141,11 @@ public class FeatureManager {
private void calculateActiveProviders() {
final Map<String, FeatureDescription> activeMap = new TreeMap<String, FeatureDescription>();
- for(final Map.Entry<String, List<FeatureDescription>> entry : this.allFeatures.entrySet()) {
+ 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 ) {
+ if (entry.getValue().size() > 1) {
logger.warn("More than one feature service for feature {}", entry.getKey());
}
}
@@ -155,13 +169,13 @@ public class FeatureManager {
public ClientContext getCurrentClientContext() {
ClientContext result = perThreadClientContext.get();
- if ( result == null ) {
+ if (result == null) {
result = defaultClientContext;
}
return result;
}
- public void setCurrentClientContext(final SlingHttpServletRequest request) {
+ public void setCurrentClientContext(final HttpServletRequest request) {
final ExecutionContext providerContext = new ExecutionContextImpl(request);
final ClientContextImpl ctx = this.createClientContext(providerContext);
perThreadClientContext.set(ctx);
@@ -172,7 +186,7 @@ public class FeatureManager {
}
public ClientContext createClientContext(final ResourceResolver resolver) {
- if ( resolver == null ) {
+ if (resolver == null) {
throw new IllegalArgumentException("Resolver must not be null.");
}
final ExecutionContext providerContext = new ExecutionContextImpl(resolver);
@@ -180,8 +194,8 @@ public class FeatureManager {
return ctx;
}
- public ClientContext createClientContext(final SlingHttpServletRequest request) {
- if ( request == null ) {
+ public ClientContext createClientContext(final HttpServletRequest request) {
+ if (request == null) {
throw new IllegalArgumentException("Request must not be null.");
}
final ExecutionContext providerContext = new ExecutionContextImpl(request);
@@ -192,10 +206,10 @@ public class FeatureManager {
private ClientContextImpl createClientContext(final ExecutionContext providerContext) {
final List<Feature> enabledFeatures = new ArrayList<Feature>();
- for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
+ for (final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
final Feature f = entry.getValue().feature;
- if ( entry.getValue().feature.isEnabled(providerContext) ) {
+ if (entry.getValue().feature.isEnabled(providerContext)) {
enabledFeatures.add(f);
}
}
@@ -206,7 +220,7 @@ public class FeatureManager {
public Feature[] getAvailableFeatures() {
final List<Feature> result = new ArrayList<Feature>();
- for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
+ for (final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) {
final Feature f = entry.getValue().feature;
result.add(f);
}
@@ -215,7 +229,7 @@ public class FeatureManager {
public Feature getFeature(final String name) {
final FeatureDescription desc = this.activeFeatures.get(name);
- if ( desc != null ) {
+ if (desc != null) {
return desc.feature;
}
return null;
@@ -230,31 +244,33 @@ public class FeatureManager {
}
/**
- * 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.
@@ -263,8 +279,8 @@ public class FeatureManager {
@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;
}
Added: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java?rev=1561613&view=auto
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java (added)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java Mon Jan 27 08:58:11 2014
@@ -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;
+ }
+ };
+ }
+}
Propchange: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeatureWebConsolePlugin.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/impl/FeaturesImpl.java Mon Jan 27 08:58:11 2014
@@ -18,7 +18,8 @@
*/
package org.apache.sling.featureflags.impl;
-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;
@@ -66,7 +67,7 @@ public class FeaturesImpl implements Fea
}
@Override
- public ClientContext createClientContext(final SlingHttpServletRequest request) {
+ public ClientContext createClientContext(final HttpServletRequest request) {
return this.manager.createClientContext(request);
}
}
Modified: sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java?rev=1561613&r1=1561612&r2=1561613&view=diff
==============================================================================
--- sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java (original)
+++ sling/whiteboard/fmeschbe/featureflags/feature-flags/src/main/java/org/apache/sling/featureflags/package-info.java Mon Jan 27 08:58:11 2014
@@ -18,10 +18,48 @@
*/
/**
- * Provides a service to interface which may be implemented by applications
- * to get notified on cluster topology changes.
+ * The <i>Feature Flags</i> feature allows applications to dynamically
+ * provide features to clients and consumers depending on various criteria such as
+ * <ul>
+ * <li>Time of Day</li>
+ * <li>Static Configuration</li>
+ * <li>Request Parameter</li>
+ * <li>Some Resource</li>
+ * </ul>
+ * <p>
+ * Feature flag support consists of two parts: The feature flag itself represented
+ * by the {@link org.apache.sling.featureflags.Feature Feature} interface and the
+ * the application providing a feature guarded by a feature flag. Such applications
+ * make use of the {@link org.apache.sling.featureflags.Features Features} service to
+ * query feature flags.
+ * <p>
+ * Feature flags can be provided by registering
+ * {@link org.apache.sling.featureflags.Feature Feature} services. Alternatively
+ * feature flags can be provided by factory configuration with factory PID
+ * {@code org.apache.sling.featureflags.Feature} as follows:
+ * <table>
+ * <tr>
+ * <td>{@code name}</td>
+ * <td>Short name of this feature. This name is used to refer to this feature
+ * when checking for it to be enabled or not. This property is required
+ * and defaults to a name derived from the feature's class name and object
+ * identity. It is strongly recommended to define a useful and unique for the feature</td>
+ * </tr>
+ * <tr>
+ * <td>{@code description}</td>
+ * <td>Description for the feature. The intent is to descibe the behaviour
+ * of the application if this feature would be enabled. It is recommended
+ * to define this property. The default value is the value of the name property.</td>
+ * </tr>
+ * <tr>
+ * <td>{@code enabled}</td>
+ * <td>Boolean flag indicating whether the feature is enabled or not by
+ * this configuration</td>
+ * </tr>
+ * </table>
*
* @version 1.0
+ * @see <a href="http://sling.apache.org/documentation/the-sling-engine/featureflags.html">Feature Flags</a>
*/
@Version("1.0")
package org.apache.sling.featureflags;