BOOKKEEPER-941: Feature Switches for controling client and server behavior

- Introduce Features that are dynamic configuration options
- Allow specifying Features as configuration parameters

This is a port of the feature switches following changes
https://github.com/twitter/bookkeeper/commit/c2a092ab9b585f1d30d9e9b9dead0533efa49855
https://github.com/twitter/bookkeeper/commit/f9762d126e311a6b129e6e169dc62c2a0bdb7b4a

Author: Robin Dhamankar <robindh@Robins-MacBook-Air.local>

Reviewers: Matteo Merli <mmerli@apache.org>, Sijie Guo <sijie@apache.org>

Closes #54 from robindh/FeatureSwitches
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/AbstractConfiguration.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/AbstractConfiguration.java
index 314290e..adf08ff 100644
--- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/AbstractConfiguration.java
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/AbstractConfiguration.java
@@ -25,6 +25,7 @@
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.commons.configuration.SystemConfiguration;
 
+import org.apache.bookkeeper.feature.Feature;
 import org.apache.bookkeeper.meta.LedgerManagerFactory;
 import org.apache.bookkeeper.util.ReflectionUtils;
 
@@ -243,4 +244,16 @@
     public void setMetastoreMaxEntriesPerScan(int maxEntries) {
         setProperty(METASTORE_MAX_ENTRIES_PER_SCAN, maxEntries);
     }
+
+    public void setFeature(String configProperty, Feature feature) {
+        setProperty(configProperty, feature);
+    }
+
+    public Feature getFeature(String configProperty, Feature defaultValue) {
+        if (null == getProperty(configProperty)) {
+            return defaultValue;
+        } else {
+            return (Feature)getProperty(configProperty);
+        }
+    }
 }
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/CacheableFeatureProvider.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/CacheableFeatureProvider.java
new file mode 100644
index 0000000..95c9981
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/CacheableFeatureProvider.java
@@ -0,0 +1,85 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Cacheable Feature Provider
+ */
+public abstract class CacheableFeatureProvider<T extends Feature> implements FeatureProvider {
+
+    protected final String scope;
+    protected final ConcurrentMap<String, FeatureProvider> scopes =
+            new ConcurrentHashMap<String, FeatureProvider>();
+    protected final ConcurrentMap<String, T> features =
+            new ConcurrentHashMap<String, T>();
+
+    protected CacheableFeatureProvider(String scope) {
+        this.scope = scope;
+    }
+
+    protected String makeName(String name) {
+        if (StringUtils.isBlank(scope)) {
+            return name;
+        } else {
+            return scope + "." + name;
+        }
+    }
+
+    @Override
+    public T getFeature(String name) {
+        T feature = features.get(name);
+        if (null == feature) {
+            T newFeature = makeFeature(makeName(name));
+            T oldFeature = features.putIfAbsent(name, newFeature);
+            if (null == oldFeature) {
+                feature = newFeature;
+            } else {
+                feature = oldFeature;
+            }
+        }
+        return feature;
+    }
+
+    protected abstract T makeFeature(String featureName);
+
+    @Override
+    public FeatureProvider scope(String name) {
+        FeatureProvider provider = scopes.get(name);
+        if (null == provider) {
+            FeatureProvider newProvider = makeProvider(makeName(name));
+            FeatureProvider oldProvider = scopes.putIfAbsent(name, newProvider);
+            if (null == oldProvider) {
+                provider = newProvider;
+            } else {
+                provider = oldProvider;
+            }
+        }
+        return provider;
+    }
+
+    protected abstract FeatureProvider makeProvider(String fullScopeName);
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/Feature.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/Feature.java
new file mode 100644
index 0000000..ab9fae9
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/Feature.java
@@ -0,0 +1,51 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+/**
+ * This interface represents a feature.
+ */
+public interface Feature {
+    public static int FEATURE_AVAILABILITY_MAX_VALUE = 100;
+
+    /**
+     * Returns a textual representation of the feature.
+     *
+     * @return name of the feature.
+     */
+    String name();
+
+    /**
+     * Returns the availability of this feature, an integer between 0 and 100.
+     *
+     * @return the availability of this feature.
+     */
+    int availability();
+
+    /**
+     * Whether this feature is available or not.
+     *
+     * @return true if this feature is available, otherwise false.
+     */
+    boolean isAvailable();
+}
+
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FeatureProvider.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FeatureProvider.java
new file mode 100644
index 0000000..04686b7
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FeatureProvider.java
@@ -0,0 +1,44 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+/**
+ * Provider to provide features.
+ */
+public interface FeatureProvider {
+    /**
+     * Return the feature with given name.
+     *
+     * @param name feature name
+     * @return feature instance
+     */
+    Feature getFeature(String name);
+
+    /**
+     * Provide the feature provider under scope <i>name</i>.
+     *
+     * @param name
+     *          scope name.
+     * @return feature provider under scope <i>name</i>
+     */
+    FeatureProvider scope(String name);
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FixedValueFeature.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FixedValueFeature.java
new file mode 100644
index 0000000..825276b
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/FixedValueFeature.java
@@ -0,0 +1,52 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+public class FixedValueFeature implements Feature {
+    protected final String name;
+    protected int availability;
+
+    public FixedValueFeature(String name, int availability) {
+        this.name = name;
+        this.availability = availability;
+    }
+
+    public FixedValueFeature(String name, boolean available) {
+        this.name = name;
+        this.availability = available ? FEATURE_AVAILABILITY_MAX_VALUE : 0;
+    }
+
+    @Override
+    public String name() {
+        return null;
+    }
+
+    @Override
+    public int availability() {
+        return availability;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return availability() > 0;
+    }
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeature.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeature.java
new file mode 100644
index 0000000..f175bf0
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeature.java
@@ -0,0 +1,41 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+public class SettableFeature extends FixedValueFeature {
+    public SettableFeature(String name, int initialAvailability) {
+        super(name, initialAvailability);
+    }
+
+    public SettableFeature(String name, boolean isAvailabile) {
+        super(name, isAvailabile);
+    }
+
+    public void set(int availability) {
+        this.availability = availability;
+    }
+
+    public void set(boolean isAvailabile) {
+        this.availability = isAvailabile ? FEATURE_AVAILABILITY_MAX_VALUE : 0;
+    }
+
+}
diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeatureProvider.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeatureProvider.java
new file mode 100644
index 0000000..e21ad9d
--- /dev/null
+++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/feature/SettableFeatureProvider.java
@@ -0,0 +1,48 @@
+package org.apache.bookkeeper.feature;
+
+/*
+ *
+ * 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.
+ *
+ */
+
+/**
+ * A provider will provide settable features.
+ */
+public class SettableFeatureProvider extends CacheableFeatureProvider<SettableFeature> {
+
+    public final static FeatureProvider DISABLE_ALL = new SettableFeatureProvider("", 0);
+
+    protected final int availability;
+
+    public SettableFeatureProvider(String scope, int availability) {
+        super(scope);
+        this.availability = availability;
+    }
+
+    @Override
+    protected SettableFeature makeFeature(String featureName) {
+        return new SettableFeature(featureName, availability);
+    }
+
+    @Override
+    protected FeatureProvider makeProvider(String fullScopeName) {
+        return new SettableFeatureProvider(fullScopeName, availability);
+    }
+
+}