diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/Feature.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/Feature.java index afad73f2e6f..7fdc1d2d06d 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/Feature.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/Feature.java @@ -22,8 +22,11 @@ import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.jackrabbit.oak.commons.properties.SystemPropertySupplier; import org.apache.jackrabbit.oak.spi.whiteboard.Registration; import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A feature toggle to control new functionality. The default state of a feature @@ -34,12 +37,20 @@ * involves registering a feature toggle on the {@link Whiteboard} and * potentially comes with some overhead (e.g. when the whiteboard is based on * OSGi). Therefore, client code should not create a new feature, check the - * state and then immediately release/close it again. Instead a feature should + * state and then immediately release/close it again. Instead, a feature should * be acquired initially, checked at runtime whenever needed and finally * released when the client component is destroyed. + *

+ * The default state of {@code false} can be overridden by a system property + * using {@linkplain #newFeatureWithSystemPropertyDefault(String, Whiteboard)}, + * in which case The property name is derived from the toggle name. This helps to quickly verify + * a new feature. For a toggle named {@code "foo"}, the system property name is + * {@code oak-feature.foo}. */ public final class Feature implements Closeable { + private static final Logger LOG = LoggerFactory.getLogger(Feature.class); + private final AtomicBoolean value; private final Registration registration; @@ -49,6 +60,16 @@ private Feature(AtomicBoolean value, Registration registration) { this.registration = registration; } + private static Feature internalNewFeatureWithSystemPropertyDefault(String name, Whiteboard whiteboard, boolean withSysPropDefault) { + // by default the initial value is false, but it can be overridden by a system property + AtomicBoolean value = withSysPropDefault ? new AtomicBoolean( + SystemPropertySupplier.create("oak-feature." + name, false). + loggingTo(LOG).get()) : new AtomicBoolean(); + FeatureToggle adapter = new FeatureToggle(name, value); + return new Feature(value, whiteboard.register( + FeatureToggle.class, adapter, Collections.emptyMap())); + } + /** * Creates a new {@link Feature} with the given name and registers the * corresponding {@link FeatureToggle} on the {@link Whiteboard}. @@ -60,10 +81,15 @@ private Feature(AtomicBoolean value, Registration registration) { * @return the feature toggle. */ public static Feature newFeature(String name, Whiteboard whiteboard) { - AtomicBoolean value = new AtomicBoolean(); - FeatureToggle adapter = new FeatureToggle(name, value); - return new Feature(value, whiteboard.register( - FeatureToggle.class, adapter, Collections.emptyMap())); + return internalNewFeatureWithSystemPropertyDefault(name, whiteboard, false); + } + + /** + * Same as {@linkplain #newFeature(String, Whiteboard)}, but with the initial state provided + * by a system property, named based on the feature's name. + */ + public static Feature newFeatureWithSystemPropertyDefault(String name, Whiteboard whiteboard) { + return internalNewFeatureWithSystemPropertyDefault(name, whiteboard, true); } /** diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/package-info.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/package-info.java index 840e64c3fcb..c1a3042e113 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/package-info.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/spi/toggle/package-info.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -@Version("1.0.0") +@Version("1.1.0") package org.apache.jackrabbit.oak.spi.toggle; import org.osgi.annotation.versioning.Version; \ No newline at end of file diff --git a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/toggle/FeatureToggleTest.java b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/toggle/FeatureToggleTest.java index 7999b04f521..8ffbe3990ca 100644 --- a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/toggle/FeatureToggleTest.java +++ b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/spi/toggle/FeatureToggleTest.java @@ -28,6 +28,7 @@ import org.junit.Test; import static org.apache.jackrabbit.oak.spi.toggle.Feature.newFeature; +import static org.apache.jackrabbit.oak.spi.toggle.Feature.newFeatureWithSystemPropertyDefault; import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.Matchers.empty; import static org.hamcrest.core.Is.is; @@ -50,6 +51,28 @@ public void disabledByDefault() { } } + @Test + public void defaultOverriddenAsTrue() { + String sysPropName = "oak-feature.my.toggle"; + System.setProperty(sysPropName, "true"); + try (Feature feature = newFeatureWithSystemPropertyDefault("my.toggle", whiteboard)) { + assertTrue(feature.isEnabled()); + } finally { + System.clearProperty(sysPropName); + } + } + + @Test + public void defaultOverriddenAsFalse() { + String sysPropName = "oak-feature.my.toggle"; + System.setProperty(sysPropName, "false"); + try (Feature feature = newFeatureWithSystemPropertyDefault("my.toggle", whiteboard)) { + assertFalse(feature.isEnabled()); + } finally { + System.clearProperty(sysPropName); + } + } + @Test public void register() { try (Feature feature = newFeature("my.toggle", whiteboard)) { diff --git a/oak-core-spi/src/test/resources/logback-test.xml b/oak-core-spi/src/test/resources/logback-test.xml index 042856c27b8..accccfcb4a0 100644 --- a/oak-core-spi/src/test/resources/logback-test.xml +++ b/oak-core-spi/src/test/resources/logback-test.xml @@ -34,4 +34,5 @@ + \ No newline at end of file