package org.ff4j.web.controller;
/*
* #%L
* ff4j-sample-web
* %%
* Copyright (C) 2013 - 2016 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 static org.ff4j.web.bean.WebConstants.DESCRIPTION;
import static org.ff4j.web.bean.WebConstants.GROUPNAME;
import static org.ff4j.web.bean.WebConstants.NEW_NAME;
import static org.ff4j.web.bean.WebConstants.OP_ADD_PERMISSION;
import static org.ff4j.web.bean.WebConstants.OP_CLEAR_PERMISSIONS;
import static org.ff4j.web.bean.WebConstants.OP_COPY_FEATURE;
import static org.ff4j.web.bean.WebConstants.OP_CREATE_FEATURE;
import static org.ff4j.web.bean.WebConstants.OP_DISABLE;
import static org.ff4j.web.bean.WebConstants.OP_EDIT_FEATURE;
import static org.ff4j.web.bean.WebConstants.OP_ENABLE;
import static org.ff4j.web.bean.WebConstants.OP_RENAME_FEATURE;
import static org.ff4j.web.bean.WebConstants.OP_RMV_FEATURE;
import static org.ff4j.web.bean.WebConstants.OP_RMV_PERMISSION;
import static org.ff4j.web.bean.WebConstants.OP_RMV_PROPERTY;
import static org.ff4j.web.bean.WebConstants.OP_TOGGLE_GROUP;
import static org.ff4j.web.bean.WebConstants.STRATEGY;
import static org.ff4j.web.bean.WebConstants.STRATEGY_INIT;
import static org.ff4j.web.bean.WebConstants.SUBOPERATION;
import static org.ff4j.web.embedded.ConsoleRenderer.msg;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ff4j.FF4j;
import org.ff4j.core.Feature;
import org.ff4j.core.FlippingStrategy;
import org.ff4j.utils.MappingUtil;
import org.ff4j.utils.Util;
import org.ff4j.web.bean.WebConstants;
import org.ff4j.web.embedded.ConsoleOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
/**
* Controller for main class
*
* @author Cedrick LUNVEN (@clunven)
*/
public class FeaturesController extends AbstractController {
/** Logger for this class. */
public static final Logger LOGGER = LoggerFactory.getLogger(FeaturesController.class);
/** View name. */
private static final String VIEW_FEATURES = "features";
/** {@inheritDoc} */
public FeaturesController(FF4j ff4j, TemplateEngine te) {
super(ff4j, VIEW_FEATURES, te);
}
/** {@inheritDoc} */
public void get(HttpServletRequest req, HttpServletResponse res, WebContext ctx) throws IOException {
String operation = req.getParameter(WebConstants.OPERATION);
String featureId = req.getParameter(WebConstants.FEATID);
String msgType = "success";
String msg = null;
if (Util.hasLength(operation) && Util.hasLength(featureId)) {
if (getFf4j().getFeatureStore().exist(featureId)) {
if (OP_DISABLE.equalsIgnoreCase(operation)) {
getFf4j().disable(featureId);
msg = msg(featureId, "DISABLED");
LOGGER.info(featureId + " has been disabled");
}
if (OP_ENABLE.equalsIgnoreCase(operation)) {
getFf4j().enable(featureId);
msg = msg(featureId, "ENABLED");
LOGGER.info(featureId + " has been enabled");
}
if (OP_ADD_PERMISSION.equalsIgnoreCase(operation)) {
String permName = req.getParameter(WebConstants.PERMISSION);
Feature feature = getFf4j().getFeatureStore().read(featureId);
feature.getPermissions().add(permName);
getFf4j().getFeatureStore().update(feature);
LOGGER.info("Add new " + permName + " to " + featureId );
}
if (OP_RMV_PERMISSION.equalsIgnoreCase(operation)) {
String permName = req.getParameter(WebConstants.PERMISSION);
Feature feature = getFf4j().getFeatureStore().read(featureId);
feature.getPermissions().remove(permName);
getFf4j().getFeatureStore().update(feature);
LOGGER.info("Remove " + permName + " to " + featureId );
}
if (OP_CLEAR_PERMISSIONS.equalsIgnoreCase(operation)) {
Feature feature = getFf4j().getFeatureStore().read(featureId);
feature.getPermissions().clear();
getFf4j().getFeatureStore().update(feature);
LOGGER.info("Clear permissions for " + featureId);
}
if (OP_RMV_PROPERTY.equalsIgnoreCase(operation)) {
String propertyName = req.getParameter(WebConstants.NAME);
Feature feature = getFf4j().getFeatureStore().read(featureId);
feature.getCustomProperties().remove(propertyName);
getFf4j().getFeatureStore().update(feature);
LOGGER.info("Remove Property " + propertyName + " to " + featureId );
}
} else {
msgType = "warning";
msg = "The feature '" + featureId + "' does not exist";
}
}
ctx.setVariable("msgType", msgType);
ctx.setVariable("msgInfo", msg);
renderPage(ctx);
}
/** {@inheritDoc} */
public void post(HttpServletRequest req, HttpServletResponse res, WebContext ctx)
throws IOException {
String msg = null;
String msgType = "success";
String operation = req.getParameter(WebConstants.OPERATION);
String featureId = req.getParameter(WebConstants.FEATID);
if (OP_EDIT_FEATURE.equalsIgnoreCase(operation)) {
this.updateFeature(req, featureId);
msg = featureId + " has been UPDATED";
} else if (OP_CREATE_FEATURE.equalsIgnoreCase(operation)) {
ConsoleOperations.createFeature(getFf4j(), req);
msg = featureId + " has been CREATED";
} else if (OP_RMV_FEATURE.equalsIgnoreCase(operation)) {
getFf4j().getFeatureStore().delete(featureId);
msg = featureId + " has been DELETED";
} else if (OP_RENAME_FEATURE.equalsIgnoreCase(operation)) {
String newName = req.getParameter(NEW_NAME);
Set< String> featureNames = getFf4j().getFeatureStore().readAll().keySet();
if (featureNames.contains(newName)) {
msgType = "warning";
msg = "Cannot rename " + featureId + " to " + newName + " : it already exists";
} else {
Feature newFeature = getFf4j().getFeatureStore().read(featureId);
newFeature.setUid(newName);
getFf4j().getFeatureStore().delete(featureId);
getFf4j().getFeatureStore().create(newFeature);
msg = "Feature " + featureId + " has been renamed to " + newName;
}
} else if (OP_COPY_FEATURE.equalsIgnoreCase(operation)) {
String newName = req.getParameter(NEW_NAME);
Set< String> featureNames = getFf4j().getFeatureStore().readAll().keySet();
if (featureNames.contains(newName)) {
msgType = "warning";
msg = "Cannot copy " + featureId + " with name " + newName + " : it already exists";
} else {
Feature newFeature = new Feature(getFf4j().getFeatureStore().read(featureId));
newFeature.setUid(newName);
getFf4j().getFeatureStore().create(newFeature);
msg = "Feature " + featureId + " has been copied to " + newName;
}
} else if (OP_TOGGLE_GROUP.equalsIgnoreCase(operation)) {
String groupName = req.getParameter(GROUPNAME);
if (groupName != null && !groupName.isEmpty()) {
String operationGroup = req.getParameter(SUBOPERATION);
if (OP_ENABLE.equalsIgnoreCase(operationGroup)) {
getFf4j().getFeatureStore().enableGroup(groupName);
msg = groupName + " has been ENABLED";
LOGGER.info("Group '" + groupName + "' has been ENABLED.");
} else if (OP_DISABLE.equalsIgnoreCase(operationGroup)) {
getFf4j().getFeatureStore().disableGroup(groupName);
msg = groupName + " has been DISABLED";
LOGGER.info("Group '" + groupName + "' has been DISABLED.");
}
}
}
ctx.setVariable("msgType", msgType);
ctx.setVariable("msgInfo", msg);
renderPage(ctx);
}
/**
* Allow to update feature.
*
* @param featureId
*/
private void updateFeature(HttpServletRequest req, String featureId) {
Feature old = ff4j.getFeatureStore().read(featureId);
// Core
Feature fp = new Feature(featureId, old.isEnable());
fp.setPermissions(old.getPermissions());
fp.setCustomProperties(old.getCustomProperties());
fp.setFlippingStrategy(buildFlippingStrategy(req, fp.getUid()));
// Description
final String featureDesc = req.getParameter(DESCRIPTION);
if (Util.hasLength(featureDesc)) {
fp.setDescription(featureDesc);
}
// GroupName
final String groupName = req.getParameter(GROUPNAME);
if (Util.hasLength(groupName)) {
fp.setGroup(groupName);
}
// Creation
ff4j.getFeatureStore().update(fp);
}
/**
* Create Flipping Strategy from parameters.
*
* @param req
* current http query
* @param uid
* unique feature identifier
* @return instance of strategy
*/
private FlippingStrategy buildFlippingStrategy(HttpServletRequest req, String uid) {
String strategy = req.getParameter(STRATEGY);
String strategyParams = req.getParameter(STRATEGY_INIT);
FlippingStrategy fstrategy = null;
Map<String, String> initParams = new HashMap<String, String>();
if (Util.hasLength(strategy)) {
if (Util.hasLength(strategyParams)) {
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 = MappingUtil.instanceFlippingStrategy(uid, strategy, initParams);
}
return fstrategy;
}
/**
* Both get and post operation will render the page.
*
* @param ctx
* current web context
*/
private void renderPage(WebContext ctx) {
ctx.setVariable(KEY_TITLE, "Features");
// Sort natural Order
Map<String, Feature> mapOfFeatures = ff4j.getFeatureStore().readAll();
List<String> featuresNames = Arrays.asList(mapOfFeatures.keySet().toArray(new String[0]));
Collections.sort(featuresNames);
List<Feature> orderedFeatures = new ArrayList<Feature>();
for (String featuName : featuresNames) {
orderedFeatures.add(mapOfFeatures.get(featuName));
}
ctx.setVariable("listOfFeatures", orderedFeatures);
// Get Group List
List<String> myGroupList = new ArrayList<String>(ff4j.getFeatureStore().readAllGroups());
Collections.sort(myGroupList);
ctx.setVariable("groupList", myGroupList);
}
}