org.ff4j.web.embedded.ConsoleOperations Maven / Gradle / Ivy
package org.ff4j.web.embedded;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_PROPERTIES;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_XML;
import static org.ff4j.web.embedded.ConsoleConstants.CONTENT_TYPE_YAML;
import static org.ff4j.web.embedded.ConsoleConstants.DESCRIPTION;
import static org.ff4j.web.embedded.ConsoleConstants.FEATID;
import static org.ff4j.web.embedded.ConsoleConstants.FORMAT_PROPERTIES;
import static org.ff4j.web.embedded.ConsoleConstants.FORMAT_XML;
import static org.ff4j.web.embedded.ConsoleConstants.FORMAT_YAML;
import static org.ff4j.web.embedded.ConsoleConstants.FORMAT_YML;
import static org.ff4j.web.embedded.ConsoleConstants.GROUPNAME;
import static org.ff4j.web.embedded.ConsoleConstants.PERMISSION;
import static org.ff4j.web.embedded.ConsoleConstants.PERMISSION_RESTRICTED;
import static org.ff4j.web.embedded.ConsoleConstants.PREFIX_CHECKBOX;
import static org.ff4j.web.embedded.ConsoleConstants.SDF;
import static org.ff4j.web.embedded.ConsoleConstants.STRATEGY;
import static org.ff4j.web.embedded.ConsoleConstants.STRATEGY_INIT;
/*
* #%L
* ff4j-web
* %%
* Copyright (C) 2013 - 2015 FF4J
* %%
* 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.
* #L%
*/
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ff4j.FF4j;
import org.ff4j.conf.FF4jConfiguration;
import org.ff4j.conf.FF4jConfigurationParser;
import org.ff4j.conf.XmlConfig;
import org.ff4j.conf.XmlParser;
import org.ff4j.core.Feature;
import org.ff4j.core.FeatureStore;
import org.ff4j.core.FlippingStrategy;
import org.ff4j.parser.properties.PropertiesParser;
import org.ff4j.parser.yaml.YamlParser;
import org.ff4j.property.Property;
import org.ff4j.property.store.PropertyStore;
import org.ff4j.property.util.PropertyFactory;
import org.ff4j.utils.Util;
import org.ff4j.web.bean.WebConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ConsoleOperations {
/** Logger for this class. */
private static Logger LOGGER = LoggerFactory.getLogger(ConsoleOperations.class);
private ConsoleOperations() {}
/**
* User action to create a new Feature.
*
* @param req
* http request containing operation parameters
*/
public static void createFeature(FF4j ff4j, HttpServletRequest req) {
// uid
final String featureId = req.getParameter(FEATID);
if (featureId != null && !featureId.isEmpty()) {
Feature fp = new Feature(featureId, false);
// Description
final String featureDesc = req.getParameter(DESCRIPTION);
if (null != featureDesc && !featureDesc.isEmpty()) {
fp.setDescription(featureDesc);
}
// GroupName
final String groupName = req.getParameter(GROUPNAME);
if (null != groupName && !groupName.isEmpty()) {
fp.setGroup(groupName);
}
// Strategy
final String strategy = req.getParameter(STRATEGY);
if (null != strategy && !strategy.isEmpty()) {
try {
Class> strategyClass = Class.forName(strategy);
FlippingStrategy fstrategy = (FlippingStrategy) strategyClass.newInstance();
final String strategyParams = req.getParameter(STRATEGY_INIT);
if (null != strategyParams && !strategyParams.isEmpty()) {
Map initParams = new HashMap();
String[] params = strategyParams.split(";");
for (String currentP : params) {
String[] cur = currentP.split("=");
if (cur.length < 2) {
throw new IllegalArgumentException("Invalid Syntax : param1=val1,val2;param2=val3,val4");
}
initParams.put(cur[0], cur[1]);
}
fstrategy.init(featureId, initParams);
}
fp.setFlippingStrategy(fstrategy);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find strategy class", e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instantiate strategy", e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot instantiate : no public constructor", e);
}
}
// Permissions
final String permission = req.getParameter(PERMISSION);
if (null != permission && PERMISSION_RESTRICTED.equals(permission)) {
@SuppressWarnings("unchecked")
Map parameters = req.getParameterMap();
Set permissions = new HashSet();
for (String key : parameters.keySet()) {
if (key.startsWith(PREFIX_CHECKBOX)) {
permissions.add(key.replace(PREFIX_CHECKBOX, ""));
}
}
fp.setPermissions(permissions);
}
// Creation
ff4j.getFeatureStore().create(fp);
LOGGER.info(featureId + " has been created");
}
}
/**
* Sample Element should be updated like name, description, value
* @param ff4j
* @param req
*/
public static void updateProperty(FF4j ff4j, HttpServletRequest req) {
String name = req.getParameter("name");
String type = req.getParameter("pType");
String description = req.getParameter("desc");
String value = req.getParameter("pValue");
String uid = req.getParameter("uid");
String featureId = req.getParameter(WebConstants.FEATURE_UID);
Property> ap;
// To update the core the uid is the name (rename, edit)
if (uid == null) {
uid = name;
}
// Update Feature property
if (Util.hasLength(featureId)) {
Feature current = ff4j.getFeatureStore().read(featureId);
ap = current.getProperty(uid);
ap.setDescription(description);
if (ap.getType().equalsIgnoreCase(type)) {
ap.setValueFromString(value);
} else {
ap = PropertyFactory.createProperty(name, type, value);
LOGGER.warn("By changing property type you loose the fixedValues, cannot evaluate ? at runtime");
}
ff4j.getFeatureStore().update(current);
} else if (ff4j.getPropertiesStore().existProperty(uid)) {
// Do not change name, just and update
if (uid.equalsIgnoreCase(name)) {
ap = ff4j.getPropertiesStore().readProperty(uid);
// just an update for the value
if (ap.getType().equalsIgnoreCase(type)) {
ap.setDescription(description);
ap.setValueFromString(value);
ff4j.getPropertiesStore().updateProperty(ap);
} else {
ap = PropertyFactory.createProperty(name, type, value);
ap.setDescription(description);
// Note : Fixed Values are LOST if type changed => cannot cast ? to T
LOGGER.warn("By changing property type you loose the fixedValues, cannot evaluate ? at runtime");
ff4j.getPropertiesStore().deleteProperty(name);
ff4j.getPropertiesStore().createProperty(ap);
}
} else {
// Name change delete and create a new
ap = PropertyFactory.createProperty(name, type, value);
ap.setDescription(description);
// Note : Fixed Values are LOST if name changed => cannot cast ? to T
LOGGER.warn("By changing property name you loose the fixedValues, cannot evaluate generics at runtime (type inference)");
ff4j.getPropertiesStore().deleteProperty(uid);
ff4j.getPropertiesStore().createProperty(ap);
}
}
}
/**
* Create new property in store.
*
* @param ff4j
* current ff4j instance.
* @param req
* current http request
*/
public static void createProperty(FF4j ff4j, HttpServletRequest req) {
String name = req.getParameter("name");
String type = req.getParameter("pType");
String description = req.getParameter("desc");
String value = req.getParameter("pValue");
String featureId = req.getParameter(WebConstants.FEATURE_UID);
Property> ap = PropertyFactory.createProperty(name, type, value);
ap.setDescription(description);
if (Util.hasLength(featureId)) {
Feature current = ff4j.getFeatureStore().read(featureId);
current.addProperty(ap);
ff4j.getFeatureStore().update(current);
} else {
ff4j.getPropertiesStore().createProperty(ap);
}
}
private static void updateFlippingStrategy(Feature fp, String strategy, String strategyParams) {
if (null != strategy && !strategy.isEmpty()) {
try {
Class> strategyClass = Class.forName(strategy);
FlippingStrategy fstrategy = (FlippingStrategy) strategyClass.newInstance();
if (null != strategyParams && !strategyParams.isEmpty()) {
Map initParams = new HashMap();
String[] params = strategyParams.split(";");
for (String currentP : params) {
String[] cur = currentP.split("=");
if (cur.length < 2) {
throw new IllegalArgumentException("Invalid Syntax : param1=val1,val2;param2=val3,val4");
}
initParams.put(cur[0], cur[1]);
}
fstrategy.init(fp.getUid(), initParams);
}
fp.setFlippingStrategy(fstrategy);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find strategy class", e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Cannot instantiate strategy", e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Cannot instantiate : no public constructor", e);
}
}
}
/**
* User action to update a target feature's description.
*
* @param req
* http request containing operation parameters
*/
public static void updateFeatureDescription(FF4j ff4j, HttpServletRequest req) {
// uid
final String featureId = req.getParameter(FEATID);
if (featureId != null && !featureId.isEmpty()) {
// https://github.com/clun/ff4j/issues/66
Feature old = ff4j.getFeatureStore().read(featureId);
Feature fp = new Feature(featureId, old.isEnable());
// <--
// Description
final String featureDesc = req.getParameter(DESCRIPTION);
if (null != featureDesc && !featureDesc.isEmpty()) {
fp.setDescription(featureDesc);
}
// GroupName
final String groupName = req.getParameter(GROUPNAME);
if (null != groupName && !groupName.isEmpty()) {
fp.setGroup(groupName);
}
// Strategy
updateFlippingStrategy(fp, req.getParameter(STRATEGY), req.getParameter(STRATEGY_INIT));
// Permissions
final String permission = req.getParameter(PERMISSION);
if (null != permission && PERMISSION_RESTRICTED.equals(permission)) {
@SuppressWarnings("unchecked")
Map parameters = req.getParameterMap();
Set permissions = new HashSet();
for (String key : parameters.keySet()) {
if (key.startsWith(PREFIX_CHECKBOX)) {
permissions.add(key.replace(PREFIX_CHECKBOX, ""));
}
}
fp.setPermissions(permissions);
}
// Creation
ff4j.getFeatureStore().update(fp);
LOGGER.info(featureId + " has been updated");
}
}
/**
* Please use importFile(FF4j ff4j, FF4jConfiguration config) and get a {@link FF4jConfiguration} with a {@link FF4jConfigurationParser}:
* - {@link XmlParser} : new XmlParser().parseConfigurationFile(InputStream)
* - {@link YamlParser}
* - {@link PropertiesParser}
*/
@Deprecated
public static void importFile(FF4j ff4j, InputStream in)
throws IOException {
importFile(ff4j, new XmlParser().parseConfigurationFile(in));
}
/**
* User action to import Features from a properties files.
*
* @param in
* inpustream from configuration file
* @throws IOException
* Error raised if the configuration cannot be read
*/
public static void importFile(FF4j ff4j, FF4jConfiguration config)
throws IOException {
if (config.isAutoCreate()) {
ff4j.setAutocreate(true);
LOGGER.info("Autocreate is now enabled.");
}
if (config.isAudit()) {
ff4j.setEnableAudit(true);
LOGGER.info("Audit is now enabled.");
}
FeatureStore store = ff4j.getFeatureStore();
Map mapsOfFeat = config.getFeatures();
for (Entry feature : mapsOfFeat.entrySet()) {
if (store.exist(feature.getKey())) {
store.update(feature.getValue());
} else {
store.create(feature.getValue());
}
}
LOGGER.info(mapsOfFeat.size() + " features have been imported.");
PropertyStore pstore = ff4j.getPropertiesStore();
Map> mapsOfProperties = config.getProperties();
for (Entry> p : mapsOfProperties.entrySet()) {
if (pstore.existProperty(p.getKey())) {
pstore.updateProperty(p.getValue());
} else {
pstore.createProperty(p.getValue());
}
}
LOGGER.info(mapsOfProperties.size() + " features have been imported.");
}
/**
* Export configuration.
*
* @param ff4j
* feature flags for java
* @param res
* http response
* @param format
* format
* @throws IOException
* error occured when exporting
*/
public static void exportConfiguration(FF4j ff4j, HttpServletResponse res, String format)
throws IOException {
if (format != null) {
InputStream in = null;
ServletOutputStream sos = null;
String fileName = "ff4j-" + SDF.format(new Date()) + ".";
try {
sos = res.getOutputStream();
if (FORMAT_XML.equals(format.toLowerCase())) {
res.setContentType(CONTENT_TYPE_XML);
res.setHeader("Content-Disposition",
"attachment; filename=\"" + fileName + FORMAT_XML + "\"");
in = new XmlParser().exportAll(new XmlConfig(ff4j));
} else if (FORMAT_YML.equals(format.toLowerCase()) ||
FORMAT_YAML.equals(format.toLowerCase())) {
res.setContentType(CONTENT_TYPE_YAML);
res.setHeader("Content-Disposition",
"attachment; filename=\"" + fileName + FORMAT_YML + "\"");
in = new YamlParser().export(new FF4jConfiguration(ff4j));
} else if (FORMAT_PROPERTIES.equals(format.toLowerCase())) {
res.setContentType(CONTENT_TYPE_PROPERTIES);
res.setHeader("Content-Disposition",
"attachment; filename=\"" + fileName + FORMAT_PROPERTIES + "\"");
in = new YamlParser().export(new FF4jConfiguration(ff4j));
}
if (in != null) {
org.apache.commons.io.IOUtils.copy(in, sos);
}
} finally {
if (in != null) {
in.close();
}
if (sos != null) {
sos.flush();
sos.close();
}
}
}
}
/**
* Build Http response when invoking export features.
*
* @param res
* http response
* @throws IOException
* error when building response
*/
public static void exportFile(FF4j ff4j, HttpServletResponse res) throws IOException {
Map features = ff4j.getFeatureStore().readAll();
InputStream in = new XmlParser().exportFeatures(features);
ServletOutputStream sos = null;
try {
sos = res.getOutputStream();
res.setContentType("text/xml");
res.setHeader("Content-Disposition", "attachment; filename=\"ff4j.xml\"");
// res.setContentLength()
org.apache.commons.io.IOUtils.copy(in, sos);
LOGGER.info(features.size() + " features have been exported.");
} finally {
if (in != null) {
in.close();
}
if (sos != null) {
sos.flush();
sos.close();
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy