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 7a1c95e2990b7102354a0521959217b115349f27 Author: Carsten Ziegeler <[email protected]> AuthorDate: Fri Jan 10 07:29:24 2014 +0000 Remove FeatureProvider interface, Features are OSGi services now - rename ProviderContext to ExecutionContext git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/feature-flags@1557045 13f79535-47bb-0310-9956-ffa450edef68 --- ...{ProviderContext.java => ExecutionContext.java} | 5 +- .../org/apache/sling/featureflags/Feature.java | 6 + .../apache/sling/featureflags/FeatureProvider.java | 39 ----- .../org/apache/sling/featureflags/Features.java | 11 +- .../apache/sling/featureflags/ResourceHiding.java | 6 +- .../sling/featureflags/ResourceTypeMapping.java | 6 +- .../sling/featureflags/impl/ClientContextImpl.java | 8 +- ...rContextImpl.java => ExecutionContextImpl.java} | 8 +- .../sling/featureflags/impl/FeatureManager.java | 159 ++++++++++----------- 9 files changed, 102 insertions(+), 146 deletions(-) diff --git a/src/main/java/org/apache/sling/featureflags/ProviderContext.java b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java similarity index 92% rename from src/main/java/org/apache/sling/featureflags/ProviderContext.java rename to src/main/java/org/apache/sling/featureflags/ExecutionContext.java index 08b400b..d01bd67 100644 --- a/src/main/java/org/apache/sling/featureflags/ProviderContext.java +++ b/src/main/java/org/apache/sling/featureflags/ExecutionContext.java @@ -25,11 +25,10 @@ import aQute.bnd.annotation.ProviderType; /** * The provider context contains all information that is passed to a - * {@link FeatureProvider} in order to check whether a feature - * is enabled. + * {@link Feature} in order to check whether a feature is enabled. */ @ProviderType -public interface ProviderContext { +public interface ExecutionContext { /** * Return the associated request if available diff --git a/src/main/java/org/apache/sling/featureflags/Feature.java b/src/main/java/org/apache/sling/featureflags/Feature.java index cedbb61..ca83b08 100644 --- a/src/main/java/org/apache/sling/featureflags/Feature.java +++ b/src/main/java/org/apache/sling/featureflags/Feature.java @@ -35,6 +35,12 @@ import aQute.bnd.annotation.ConsumerType; public interface Feature extends Adaptable { /** + * Checks whether the feature is enabled for the current execution + * context. + */ + boolean isEnabled(ExecutionContext context); + + /** * The name of the feature. */ String getName(); diff --git a/src/main/java/org/apache/sling/featureflags/FeatureProvider.java b/src/main/java/org/apache/sling/featureflags/FeatureProvider.java deleted file mode 100644 index 704246e..0000000 --- a/src/main/java/org/apache/sling/featureflags/FeatureProvider.java +++ /dev/null @@ -1,39 +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 aQute.bnd.annotation.ConsumerType; - -/** - * A feature provider activates one more features. - */ -@ConsumerType -public interface FeatureProvider { - - /** - * Checks whether the feature is enabled for the current execution - * context. - */ - boolean isEnabled(Feature feature, ProviderContext context); - - /** - * Return the list of available features from this provider. - */ - Feature[] getFeatures(); -} diff --git a/src/main/java/org/apache/sling/featureflags/Features.java b/src/main/java/org/apache/sling/featureflags/Features.java index 1b054a9..8b6a810 100644 --- a/src/main/java/org/apache/sling/featureflags/Features.java +++ b/src/main/java/org/apache/sling/featureflags/Features.java @@ -33,13 +33,13 @@ public interface Features { /** * Get the list of all available feature names. A feature is available - * if there is a {@link FeatureProvider} + * if there is a registered {@link Feature} service. */ String[] getAvailableFeatureNames(); /** * Get the list of all available features. A feature is available - * if there is a {@link FeatureProvider} + * if there is a registered {@link Feature} service. */ Feature[] getAvailableFeatures(); @@ -51,13 +51,14 @@ public interface Features { /** * Checks whether a feature with the given name is available. - * A feature is available if there is a {@link FeatureProvider} - * for that feature. + * A feature is available if there is a registered {@link Feature} service. */ boolean isAvailable(String featureName); /** - * Returns the current client context, if available + * Returns the current client context. + * This method always returns a client context object + * @return A client context. */ ClientContext getCurrentClientContext(); diff --git a/src/main/java/org/apache/sling/featureflags/ResourceHiding.java b/src/main/java/org/apache/sling/featureflags/ResourceHiding.java index 7bd21ba..d19fab6 100644 --- a/src/main/java/org/apache/sling/featureflags/ResourceHiding.java +++ b/src/main/java/org/apache/sling/featureflags/ResourceHiding.java @@ -31,11 +31,11 @@ public interface ResourceHiding { /** * Checks whether a resource should be hidden for a feature. - * This check is only executed if {@link FeatureProvider#isEnabled(Feature, ExecutionContext)} + * 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 FeatureProvider#isEnabled(Feature, ExecutionContext)} + * method must ensure to call {@link Feature#isEnabled(ExecutionContext)} * before calling this method and only call this method if - * {@link FeatureProvider#isEnabled(Feature, ExecutionContext)} returned <code>true</code> + * {@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 index 7f6f3c6..a9ca5f4 100644 --- a/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java +++ b/src/main/java/org/apache/sling/featureflags/ResourceTypeMapping.java @@ -31,11 +31,11 @@ public interface ResourceTypeMapping { /** * Returns the resource type mapping for a feature. - * This mapping is only executed if {@link FeatureProvider#isEnabled(Feature, ExecutionContext)} + * 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 FeatureProvider#isEnabled(Feature, ExecutionContext)} + * method must ensure to call {@link Feature#isEnabled(ExecutionContext)} * before calling this method and only call this method if - * {@link FeatureProvider#isEnabled(Feature, ExecutionContext)} returned <code>true</code> + * {@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 03b0506..b71ebe4 100644 --- a/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java +++ b/src/main/java/org/apache/sling/featureflags/impl/ClientContextImpl.java @@ -27,7 +27,7 @@ import java.util.Map; import org.apache.sling.featureflags.ClientContext; import org.apache.sling.featureflags.Feature; -import org.apache.sling.featureflags.ProviderContext; +import org.apache.sling.featureflags.ExecutionContext; import org.apache.sling.featureflags.ResourceHiding; import org.apache.sling.featureflags.ResourceTypeMapping; @@ -36,7 +36,7 @@ import org.apache.sling.featureflags.ResourceTypeMapping; */ public class ClientContextImpl implements ClientContext { - private final ProviderContext featureContext; + private final ExecutionContext featureContext; private final List<Feature> enabledFeatures; @@ -44,7 +44,7 @@ public class ClientContextImpl implements ClientContext { private final Map<String, String> mapperFeatures = new HashMap<String, String>(); - public ClientContextImpl(final ProviderContext featureContext, final List<Feature> features) { + 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) { @@ -62,7 +62,7 @@ public class ClientContextImpl implements ClientContext { this.featureContext = featureContext; } - public ProviderContext getFeatureContext() { + public ExecutionContext getFeatureContext() { return this.featureContext; } diff --git a/src/main/java/org/apache/sling/featureflags/impl/ProviderContextImpl.java b/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java similarity index 85% rename from src/main/java/org/apache/sling/featureflags/impl/ProviderContextImpl.java rename to src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java index e162b16..555575f 100644 --- a/src/main/java/org/apache/sling/featureflags/impl/ProviderContextImpl.java +++ b/src/main/java/org/apache/sling/featureflags/impl/ExecutionContextImpl.java @@ -20,23 +20,23 @@ package org.apache.sling.featureflags.impl; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.ResourceResolver; -import org.apache.sling.featureflags.ProviderContext; +import org.apache.sling.featureflags.ExecutionContext; /** * Implementation of the provider context. */ -public class ProviderContextImpl implements ProviderContext { +public class ExecutionContextImpl implements ExecutionContext { private final ResourceResolver resourceResolver; private final SlingHttpServletRequest request; - public ProviderContextImpl(final ResourceResolver resourceResolver) { + public ExecutionContextImpl(final ResourceResolver resourceResolver) { this.request = null; this.resourceResolver = resourceResolver; } - public ProviderContextImpl(final SlingHttpServletRequest request) { + public ExecutionContextImpl(final SlingHttpServletRequest request) { this.request = request; this.resourceResolver = request.getResourceResolver(); } 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 c9813e3..fd601f6 100644 --- a/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java +++ b/src/main/java/org/apache/sling/featureflags/impl/FeatureManager.java @@ -19,6 +19,7 @@ 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; @@ -33,110 +34,107 @@ import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.featureflags.ClientContext; import org.apache.sling.featureflags.Feature; -import org.apache.sling.featureflags.FeatureProvider; import org.apache.sling.featureflags.Features; -import org.apache.sling.featureflags.ProviderContext; +import org.apache.sling.featureflags.ExecutionContext; import org.osgi.framework.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This service implements the feature handling. - * It keeps track of all {@link FeatureProvider} services. + * It keeps track of all {@link Feature} services. */ @Component -@Reference(name="featureProvider", +@Reference(name="feature", cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC, - referenceInterface=FeatureProvider.class) + referenceInterface=Feature.class) public class FeatureManager implements Features { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final Map<String, List<FeatureProviderDescription>> providers = new HashMap<String, List<FeatureProviderDescription>>(); + private final Map<String, List<FeatureDescription>> allFeatures = new HashMap<String, List<FeatureDescription>>(); - private Map<String, FeatureDescription> activeProviders = new TreeMap<String, FeatureDescription>(); + private Map<String, FeatureDescription> activeFeatures = new TreeMap<String, FeatureDescription>(); /** - * Bind a new feature provider + * Bind a new feature */ - protected void bindFeatureProvider(final FeatureProvider provider, final Map<String, Object> props) { - final Feature[] features = provider.getFeatures(); - if ( features != null && features.length > 0 ) { - synchronized ( this.providers ) { - boolean changed = false; - for(final Feature f : features) { - final String name = f.getName(); - final FeatureProviderDescription info = new FeatureProviderDescription(provider, props, f); - - List<FeatureProviderDescription> candidates = this.providers.get(name); - if ( candidates == null ) { - candidates = new ArrayList<FeatureProviderDescription>(); - this.providers.put(name, candidates); - } - candidates.add(info); - Collections.sort(candidates); - changed = true; - } - if ( changed ) { - this.calculateActiveProviders(); - } + protected 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 ) { + candidates = new ArrayList<FeatureDescription>(); + this.allFeatures.put(name, candidates); } + candidates.add(info); + Collections.sort(candidates); + + this.calculateActiveProviders(); } } /** - * Unbind a feature provider + * Unbind a feature */ - protected void unbindFeatureProvider(final FeatureProvider provider, final Map<String, Object> props) { - final Feature[] features = provider.getFeatures(); - if ( features != null && features.length > 0 ) { - synchronized ( this.providers ) { - boolean changed = false; - for(final Feature f : features) { - final String name = f.getName(); - final FeatureProviderDescription info = new FeatureProviderDescription(provider, props, f); - - final List<FeatureProviderDescription> candidates = this.providers.get(name); - if ( candidates != null ) { // sanity check - candidates.remove(info); - if ( candidates.size() == 0 ) { - this.providers.remove(name); - changed = true; - } - } - } - if ( changed ) { - this.calculateActiveProviders(); + protected 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 + candidates.remove(info); + if ( candidates.size() == 0 ) { + this.allFeatures.remove(name); } } + this.calculateActiveProviders(); } } private void calculateActiveProviders() { final Map<String, FeatureDescription> activeMap = new TreeMap<String, FeatureDescription>(); - for(final Map.Entry<String, List<FeatureProviderDescription>> entry : this.providers.entrySet()) { - final FeatureProviderDescription desc = entry.getValue().get(0); - final FeatureDescription info = new FeatureDescription(); - info.feature = desc.feature; - info.provider = desc.provider; - activeMap.put(entry.getKey(), info); + 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 ) { - logger.warn("More than one feature provider for feature {}", entry.getKey()); + logger.warn("More than one feature service for feature {}", entry.getKey()); } } - this.activeProviders = activeMap; + 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(); + } + }; + @Override public ClientContext getCurrentClientContext() { - return perThreadClientContext.get(); + ClientContext result = perThreadClientContext.get(); + if ( result == null ) { + result = defaultClientContext; + } + return result; } public void setCurrentClientContext(final SlingHttpServletRequest request) { - final ProviderContext providerContext = new ProviderContextImpl(request); + final ExecutionContext providerContext = new ExecutionContextImpl(request); final ClientContextImpl ctx = this.createClientContext(providerContext); perThreadClientContext.set(ctx); } @@ -150,7 +148,7 @@ public class FeatureManager implements Features { if ( resolver == null ) { throw new IllegalArgumentException("Resolver must not be null."); } - final ProviderContext providerContext = new ProviderContextImpl(resolver); + final ExecutionContext providerContext = new ExecutionContextImpl(resolver); final ClientContext ctx = this.createClientContext(providerContext); return ctx; } @@ -160,18 +158,18 @@ public class FeatureManager implements Features { if ( request == null ) { throw new IllegalArgumentException("Request must not be null."); } - final ProviderContext providerContext = new ProviderContextImpl(request); + final ExecutionContext providerContext = new ExecutionContextImpl(request); final ClientContext ctx = this.createClientContext(providerContext); return ctx; } - private ClientContextImpl createClientContext(final ProviderContext providerContext) { + private ClientContextImpl createClientContext(final ExecutionContext providerContext) { final List<Feature> enabledFeatures = new ArrayList<Feature>(); - for(final Map.Entry<String, FeatureDescription> entry : this.activeProviders.entrySet()) { + for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) { final Feature f = entry.getValue().feature; - if ( entry.getValue().provider.isEnabled(f, providerContext) ) { + if ( entry.getValue().feature.isEnabled(providerContext) ) { enabledFeatures.add(f); } } @@ -183,7 +181,7 @@ public class FeatureManager implements Features { @Override public Feature[] getAvailableFeatures() { final List<Feature> result = new ArrayList<Feature>(); - for(final Map.Entry<String, FeatureDescription> entry : this.activeProviders.entrySet()) { + for(final Map.Entry<String, FeatureDescription> entry : this.activeFeatures.entrySet()) { final Feature f = entry.getValue().feature; result.add(f); } @@ -192,7 +190,7 @@ public class FeatureManager implements Features { @Override public Feature getFeature(final String name) { - final FeatureDescription desc = this.activeProviders.get(name); + final FeatureDescription desc = this.activeFeatures.get(name); if ( desc != null ) { return desc.feature; } @@ -201,28 +199,25 @@ public class FeatureManager implements Features { @Override public String[] getAvailableFeatureNames() { - return this.activeProviders.keySet().toArray(new String[this.activeProviders.size()]); + return this.activeFeatures.keySet().toArray(new String[this.activeFeatures.size()]); } @Override public boolean isAvailable(final String featureName) { - return this.activeProviders.containsKey(featureName); + return this.activeFeatures.containsKey(featureName); } /** - * Internal class caching some provider infos like service id and ranking. + * Internal class caching some feature meta data like service id and ranking. */ - private final static class FeatureProviderDescription implements Comparable<FeatureProviderDescription> { + private final static class FeatureDescription implements Comparable<FeatureDescription> { - public final FeatureProvider provider; public final int ranking; public final long serviceId; public final Feature feature; - public FeatureProviderDescription(final FeatureProvider provider, - final Map<String, Object> props, - final Feature feature) { - this.provider = provider; + 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)) { @@ -234,7 +229,7 @@ public class FeatureManager implements Features { } @Override - public int compareTo(final FeatureProviderDescription o) { + public int compareTo(final FeatureDescription o) { if ( this.ranking < o.ranking ) { return 1; } else if (this.ranking > o.ranking ) { @@ -246,8 +241,8 @@ public class FeatureManager implements Features { @Override public boolean equals(final Object obj) { - if ( obj instanceof FeatureProviderDescription ) { - return ((FeatureProviderDescription)obj).serviceId == this.serviceId; + if ( obj instanceof FeatureDescription ) { + return ((FeatureDescription)obj).serviceId == this.serviceId; } return false; } @@ -260,10 +255,4 @@ public class FeatureManager implements Features { return result; } } - - private final static class FeatureDescription { - public Feature feature; - public FeatureProvider provider; - - } } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
