org.ff4j.console.controller.FeaturesController.java Source code

Java tutorial

Introduction

Here is the source code for org.ff4j.console.controller.FeaturesController.java

Source

package org.ff4j.console.controller;

/*
 * #%L
 * ff4j-console
 * %%
 * Copyright (C) 2013 - 2014 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.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.ff4j.console.ApplicationConstants;
import org.ff4j.console.client.ConsoleHttpClient;
import org.ff4j.console.domain.EnvironmenBean;
import org.ff4j.console.domain.FeaturesBean;
import org.ff4j.core.Feature;
import org.ff4j.core.FeatureStore;
import org.ff4j.core.FeatureXmlParser;
import org.ff4j.core.FlippingStrategy;
import org.ff4j.web.api.FF4jWebConstants;
import org.ff4j.web.store.FeatureStoreHttp;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

/**
 * Controller to display features.
 *
 * @author <a href="mailto:cedrick.lunven@gmail.com">Cedrick LUNVEN</a>
 */
@Controller
@RequestMapping("/" + ApplicationConstants.VIEW_FEATURES + ".do")
public class FeaturesController extends AbstractConsoleController {

    /**
     * Display screen
     */
    @RequestMapping(method = RequestMethod.GET)
    public ModelAndView showPage(HttpServletRequest req, HttpServletResponse res,
            @RequestParam(value = OPERATION, required = false) String operation,
            @RequestParam(value = FEATID, required = false) String featureId) throws Exception {

        // Environment de travail
        EnvironmenBean envBean = (EnvironmenBean) req.getSession().getAttribute(ATTR_ENVBEAN);
        if (null == envBean) {
            return new ModelAndView("redirect:loadConfig.do");
        }
        log.info("Page <FEATURES>, action<GET>, env<" + envBean.getEnvId() + ">");

        // Access features through HTTP store (all parsing done)
        FeatureStore storeHTTP = new FeatureStoreHttp(envBean.getEnvUrl() + "/" + FF4jWebConstants.RESOURCE_FF4J);

        // Data in the target screen
        FeaturesBean featBean = new FeaturesBean();

        try {

            // Check parameter to know what to do
            if (operation != null && !operation.isEmpty()) {
                log.debug("Performing operation <" + operation + "> on <" + featureId + ">");
                if (OP_DISABLE.equalsIgnoreCase(operation)) {
                    opDisableFeature(storeHTTP, featureId);
                    String msg = buildMessage(featureId, "DISABLED");
                    featBean.setMessage(msg);
                    log.info(msg);
                } else if (OP_ENABLE.equalsIgnoreCase(operation)) {
                    opEnableFeature(storeHTTP, featureId);
                    featBean.setMessage(buildMessage(featureId, "ENABLED"));
                } else if (OP_RMV_FEATURE.equalsIgnoreCase(operation)) {
                    opDeleteFeature(storeHTTP, featureId);
                    String msg = buildMessage(featureId, "DELETED");
                    featBean.setMessage(msg);
                    log.info(msg);
                } else if (OP_EXPORT.equalsIgnoreCase(operation)) {
                    opExportFile(storeHTTP, res);
                    featBean.setMessage("Feature have been success fully exported");
                }
            }

        } catch (Exception e) {
            featBean.setMessageType(MSG_ERROR);
            featBean.setMessage(e.getMessage());
            log.error("Error when retrieving features ", e);
        }

        // Updating Bean
        featBean.setListOfFeatures(new ArrayList<Feature>(storeHTTP.readAll().values()));
        featBean.setHtmlPermissions(populatePermissionList(envBean));
        featBean.setGroupList(storeHTTP.readAllGroups());

        // Create view
        ModelAndView mav = new ModelAndView(VIEW_FEATURES);
        mav.addObject(ATTR_ENVBEAN, envBean);
        mav.addObject(ATTR_FEATUREBEAN, featBean);
        return mav;
    }

    @RequestMapping(method = RequestMethod.POST)
    public ModelAndView executeThenShowPage(HttpServletRequest req, HttpServletResponse res) throws Exception {
        log.info("Page <FEATURES> on action <POST>");

        // Environment de travail
        EnvironmenBean envBean = (EnvironmenBean) req.getSession().getAttribute(ATTR_ENVBEAN);

        // Access features through HTTP store (all parsing done)
        FeatureStore storeHTTP = new FeatureStoreHttp(envBean.getEnvUrl() + "/" + FF4jWebConstants.RESOURCE_FF4J);

        // Data in the target screen
        FeaturesBean featBean = new FeaturesBean();
        try {
            // Import Features
            if (ServletFileUpload.isMultipartContent(req)) {
                List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(req);
                for (FileItem item : items) {
                    if (item.isFormField()) {
                        if (OPERATION.equalsIgnoreCase(item.getFieldName())) {
                            log.info("Processing action : " + item.getString());
                        }
                    } else if (FLIPFILE.equalsIgnoreCase(item.getFieldName())) {
                        String filename = FilenameUtils.getName(item.getName());
                        if (filename.toLowerCase().endsWith("xml")) {
                            opImportFile(storeHTTP, item.getInputStream());
                            featBean.setMessage("The file <b>" + filename + "</b> has been successfully imported");
                        } else {
                            featBean.setMessage("Invalid FILE, must be XML files");
                            featBean.setMessageType(MSG_ERROR);
                        }
                    }
                }

            } else {
                // Edit Operations
                String operation = req.getParameter(OPERATION);
                if (operation != null && !operation.isEmpty()) {

                    if (OP_EDIT_FEATURE.equalsIgnoreCase(operation)) {
                        opUpdateFeatureDescription(storeHTTP, req);
                        featBean.setMessage(buildMessage(req.getParameter(FEATID), "UPDATED"));

                    } else if (OP_CREATE_FEATURE.equalsIgnoreCase(operation)) {
                        opCreateFeature(storeHTTP, req);
                        featBean.setMessage(buildMessage(req.getParameter(FEATID), "ADDED"));

                    } 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)) {
                                storeHTTP.enableGroup(groupName);
                                featBean.setMessage(buildMessageGroup(groupName, "ENABLED"));
                                log.info("Group '" + groupName + "' has been ENABLED.");
                            } else if (OP_DISABLE.equalsIgnoreCase(operationGroup)) {
                                storeHTTP.disableGroup(groupName);
                                featBean.setMessage(buildMessageGroup(groupName, "DISABLED"));
                                log.info("Group '" + groupName + "' has been DISABLED.");
                            }
                        }
                    } else {
                        log.error("Invalid POST OPERATION" + operation);
                        featBean.setMessageType(MSG_ERROR);
                        featBean.setMessage("Invalid REQUEST");
                    }
                }
            }
        } catch (Exception e) {
            featBean.setMessageType(MSG_ERROR);
            featBean.setMessage(e.getMessage());
            e.printStackTrace();
        }

        // Updating bean
        featBean.setListOfFeatures(new ArrayList<Feature>(storeHTTP.readAll().values()));
        featBean.setHtmlPermissions(populatePermissionList(envBean));
        featBean.setGroupList(storeHTTP.readAllGroups());

        // Create view
        ModelAndView mav = new ModelAndView(VIEW_FEATURES);
        mav.addObject(ATTR_ENVBEAN, envBean);
        mav.addObject(ATTR_FEATUREBEAN, featBean);
        return mav;
    }

    /**
     * Access the authorizationManager.
     */
    private String populatePermissionList(EnvironmenBean envBean) {
        ConsoleHttpClient client = new ConsoleHttpClient(getConnection(envBean.getEnvId()));
        StringBuilder sb = new StringBuilder("<br/>");
        for (String permission : client.getAllPermissions()) {
            sb.append("\r\n<br/>&nbsp;&nbsp;&nbsp;<input type=\"checkbox\" ");
            sb.append(" name=\"" + PREFIX_CHECKBOX + permission + "\"");
            sb.append(" id=\"" + PREFIX_CHECKBOX + permission + "\" >&nbsp;");
            sb.append(permission);
        }
        log.info(client.getAllPermissions().size() + " permission(s) retrieved");
        return sb.toString();
    }

    /**
     * Build info messages.
     * 
     * @param featureName
     *            target feature name
     * @param operationd
     *            target operationId
     * @return
     */
    private String buildMessage(String featureName, String operationId) {
        return String.format("Feature <b>%s</b> has been successfully %s", featureName, operationId);
    }

    /**
     * Build info messages.
     * 
     * @param featureName
     *            target feature name
     * @param operationd
     *            target operationId
     * @return
     */
    private String buildMessageGroup(String groupName, String operationId) {
        return String.format("Group <b>%s</b> has been successfully %s", groupName, operationId);
    }

    /**
     * User action to enable a Feature.
     * 
     * @param req
     *            http request containing operation parameters
     */
    private void opEnableFeature(FeatureStore store, String featureId) {
        if (featureId != null && !featureId.isEmpty()) {
            store.enable(featureId);
            log.info(featureId + " has been successfully enabled");
        }
    }

    /**
     * User action to disable a Feature.
     * 
     * @param req
     *            http request containing operation parameters
     */
    private void opDisableFeature(FeatureStore store, String featureId) {
        if (featureId != null && !featureId.isEmpty()) {
            store.disable(featureId);
            log.info(featureId + " has been disabled");
        }
    }

    /**
     * User action to create a new Feature.
     * 
     * @param req
     *            http request containing operation parameters
     */
    private void opCreateFeature(FeatureStore store, 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<String, String> initParams = new HashMap<String, String>();
                        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 instanciate strategy", e);
                } catch (IllegalAccessException e) {
                    throw new IllegalArgumentException("Cannot instanciate : no public constructor", e);
                }
            }

            // Permissions
            final String permission = req.getParameter(PERMISSION);
            if (null != permission && PERMISSION_RESTRICTED.equals(permission)) {
                Map<String, String[]> parameters = req.getParameterMap();
                Set<String> permissions = new HashSet<String>();
                for (String key : parameters.keySet()) {
                    if (key.startsWith(PREFIX_CHECKBOX)) {
                        permissions.add(key.replace(PREFIX_CHECKBOX, ""));
                    }
                }
                fp.setPermissions(permissions);
            }

            // Creation
            store.create(fp);
            log.info(featureId + " has been created");
        }
    }

    /**
     * User action to update a target feature's description.
     * 
     * @param req
     *            http request containing operation parameters
     */
    private void opUpdateFeatureDescription(FeatureStore store, 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<String, String> initParams = new HashMap<String, String>();
                        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 instanciate strategy", e);
                } catch (IllegalAccessException e) {
                    throw new IllegalArgumentException("Cannot instanciate : no public constructor", e);
                }
            }

            // Permissions
            final String permission = req.getParameter(PERMISSION);
            if (null != permission && PERMISSION_RESTRICTED.equals(permission)) {
                Map<String, String[]> parameters = req.getParameterMap();
                Set<String> permissions = new HashSet<String>();
                for (String key : parameters.keySet()) {
                    if (key.startsWith(PREFIX_CHECKBOX)) {
                        permissions.add(key.replace(PREFIX_CHECKBOX, ""));
                    }
                }
                fp.setPermissions(permissions);
            }

            // Creation
            store.update(fp);
            log.info(featureId + " has been updated");
        }
    }

    /**
     * User action to delete a new Feature.
     * 
     * @param req
     *            http request containing operation parameters
     */
    private void opDeleteFeature(FeatureStore store, String id) {
        if (StringUtils.hasLength(id)) {
            store.delete(id);
            log.info(id + " has been deleted");
        }
    }

    /**
     * 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
     */
    private void opImportFile(FeatureStore store, InputStream in) throws IOException {
        Map<String, Feature> mapsOfFeat = new FeatureXmlParser().parseConfigurationFile(in);
        for (Entry<String, Feature> feature : mapsOfFeat.entrySet()) {
            if (store.exist(feature.getKey())) {
                store.update(feature.getValue());
            } else {
                store.create(feature.getValue());
            }
        }
        log.info(mapsOfFeat.size() + " features have been imported.");
    }

    /**
     * Build Http response when invoking export features.
     * 
     * @param res
     *            http response
     * @throws IOException
     *             error when building response
     */
    private void opExportFile(FeatureStore store, HttpServletResponse res) throws IOException {
        Map<String, Feature> features = store.readAll();
        InputStream in = new FeatureXmlParser().exportFeatures(features);
        ServletOutputStream sos = null;
        try {
            sos = res.getOutputStream();
            res.setContentType("text/xml");
            res.setHeader("Content-Disposition", "attachment; filename=\"ff4j.xml\"");
            // res.setContentLength()
            byte[] bbuf = new byte[4096];
            int length = 0;
            while ((in != null) && (length != -1)) {
                length = in.read(bbuf);
                sos.write(bbuf, 0, length);
            }
            log.info(features.size() + " features have been exported.");
        } finally {
            if (in != null) {
                in.close();
            }
            if (sos != null) {
                sos.flush();
                sos.close();
            }
        }
    }

}