Java tutorial
/* * Copyright 2013-2016 Blocks4J Team (www.blocks4j.org) * * Licensed 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.blocks4j.feature.toggle.factory; import org.apache.commons.lang3.StringUtils; import org.blocks4j.feature.toggle.FeatureToggleConfiguration; import org.blocks4j.feature.toggle.exception.FeatureToggleFactoryException; import org.blocks4j.feature.toggle.proxy.Feature; import org.blocks4j.feature.toggle.proxy.ProbabilisticFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class FeatureToggleFactory { private static Collection<Feature> toggleList = Collections.synchronizedCollection(new HashSet<Feature>()); private static final Logger LOGGER = LoggerFactory.getLogger(FeatureToggleFactory.class); private FeatureToggleConfiguration config; private final boolean allowProbabilisticFeatures; private FeatureToggleFactory(FeatureToggleConfiguration config, boolean allowProbabilisticFeatures) { this.config = config; this.allowProbabilisticFeatures = allowProbabilisticFeatures; } public static Collection<Feature> getToggleList() { return Collections.unmodifiableCollection(toggleList); } private <T> T createFeatureProxy(String featureName, Class<? super T> commonInterface, T featureOn, T featureOff) { this.validateParams(featureName, commonInterface, featureOn, featureOff); Feature<T> feature; if (this.allowProbabilisticFeatures) { feature = new ProbabilisticFeature<T>(); } else { feature = new Feature<T>(); } feature.setConfig(this.config); feature.setCommonsInterface(commonInterface); feature.setOn(featureOn); feature.setOff(featureOff); feature.setName(featureName); feature.init(); @SuppressWarnings("unchecked") T proxy = (T) Proxy.newProxyInstance(commonInterface.getClassLoader(), new Class[] { commonInterface }, feature); LOGGER.info(String.format("Feature [%s] initialized , Object for ON is [%s] and Object for OFF is [%s]", featureName, featureOn.getClass().getSimpleName(), featureOff.getClass().getSimpleName())); toggleList.add(feature); return proxy; } private void validateParams(String featureName, Class<?> commonInterface, Object featureOn, Object featureOff) { if (!this.isFeatureNameValid(featureName)) { throw new FeatureToggleFactoryException("The featureName mustn't be null."); } if (!this.isCommonInterfaceValid(commonInterface)) { throw new FeatureToggleFactoryException("The commonInterface must be a inteface."); } if (this.isNotObjectValid(commonInterface, featureOn)) { throw new FeatureToggleFactoryException( String.format("The featureOn have to implement [%s] ", commonInterface.getName())); } if (this.isNotObjectValid(commonInterface, featureOff)) { throw new FeatureToggleFactoryException( String.format("The featureOff have to implement [%s] ", commonInterface.getName())); } } private boolean isFeatureNameValid(String featureName) { return !StringUtils.isEmpty(featureName); } private boolean isObjectValid(Class<?> commonInterface, Object feature) { return (commonInterface != null) && (feature != null) && commonInterface.isAssignableFrom(feature.getClass()); } private boolean isNotObjectValid(Class<?> commonInterface, Object feature) { return !this.isObjectValid(commonInterface, feature); } private boolean isCommonInterfaceValid(Class<?> commonInterface) { return (commonInterface != null) && commonInterface.isInterface(); } public static <T> Builder<T> forFeature(FeatureToggleConfiguration config, String featureName, Class<? super T> commonInterface) { return new Builder<T>(config, featureName, commonInterface); } public static <T> SwitchableFeatureBuilder<T> forSwitchableFeaturesConfiguration( FeatureToggleConfiguration config, Class<? super T> commonInterface) { return new SwitchableFeatureBuilder<T>(config, commonInterface); } public static class Builder<T> { private SwitchableFeatureBuilder<T> switchableFeatureBuilder; private String featureName; private Builder(FeatureToggleConfiguration config, String featureName, Class<? super T> commonInterface) { this.switchableFeatureBuilder = new SwitchableFeatureBuilder<T>(config, commonInterface); this.featureName = featureName; } public Builder<T> whenEnabled(T featureOn) { this.switchableFeatureBuilder.when(this.featureName, featureOn); return this; } public Builder<T> whenDisabled(T featureOff) { this.switchableFeatureBuilder.defaultFeature(featureOff); return this; } public Builder<T> allowProbabilisticFeatures() { this.switchableFeatureBuilder.allowProbabilisticFeatures(); return this; } public T build() { return this.switchableFeatureBuilder.build(); } } public static class SwitchableFeatureBuilder<T> { private FeatureToggleConfiguration config; private Class<? super T> commonInterface; private T defaultFeatureImpl; private LinkedHashMap<String, T> cases; private boolean allowProbabilisticFeatures; public SwitchableFeatureBuilder(FeatureToggleConfiguration config, Class<? super T> commonInterface) { this.config = config; this.commonInterface = commonInterface; this.cases = new LinkedHashMap<String, T>(4); this.allowProbabilisticFeatures = false; } public SwitchableFeatureBuilder<T> when(String featureName, T featureImpl) { this.cases.put(featureName, featureImpl); return this; } public SwitchableFeatureBuilder<T> defaultFeature(T defaultFeatureImpl) { this.defaultFeatureImpl = defaultFeatureImpl; return this; } private SwitchableFeatureBuilder<T> allowProbabilisticFeatures() { this.allowProbabilisticFeatures = true; return this; } public T build() { if (this.cases.isEmpty()) { throw new IllegalStateException(); } FeatureToggleFactory featureToggleFactory = new FeatureToggleFactory(this.config, this.allowProbabilisticFeatures); T main = null; T next; List<Map.Entry<String, T>> featuresInformation = new ArrayList<Map.Entry<String, T>>( this.cases.entrySet()); for (int i = featuresInformation.size() - 1; i >= 0; i--) { if (main == null) { next = this.defaultFeatureImpl; } else { next = main; } Map.Entry<String, T> featureInformation = featuresInformation.get(i); main = featureToggleFactory.createFeatureProxy(featureInformation.getKey(), this.commonInterface, featureInformation.getValue(), next); } return main; } } }