controllers.admin.PluginManagerController.java Source code

Java tutorial

Introduction

Here is the source code for controllers.admin.PluginManagerController.java

Source

/*! LICENSE
 *
 * Copyright (c) 2015, The Agile Factory SA and/or its affiliates. All rights
 * reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package controllers.admin;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.tuple.Pair;

import com.avaje.ebean.ExpressionList;

import be.objectify.deadbolt.java.actions.Group;
import be.objectify.deadbolt.java.actions.Restrict;
import be.objectify.deadbolt.java.actions.SubjectPresent;
import constants.IMafConstants;
import controllers.ControllersUtils;
import framework.commons.IFrameworkConstants.Syntax;
import framework.commons.message.EventMessage;
import framework.commons.message.EventMessage.MessageType;
import framework.security.ISecurityService;
import framework.services.account.AccountManagementException;
import framework.services.account.IPreferenceManagerPlugin;
import framework.services.configuration.II18nMessagesPlugin;
import framework.services.ext.api.IExtensionDescriptor.IPluginConfigurationBlockDescriptor;
import framework.services.ext.api.IExtensionDescriptor.IPluginConfigurationBlockDescriptor.ConfigurationBlockEditionType;
import framework.services.ext.api.IExtensionDescriptor.IPluginDescriptor;
import framework.services.plugins.IEventBroadcastingService;
import framework.services.plugins.IPluginManagerService;
import framework.services.plugins.IPluginManagerService.IPluginInfo;
import framework.services.plugins.IPluginManagerService.PluginStatus;
import framework.services.plugins.api.IPluginActionDescriptor;
import framework.services.plugins.api.PluginException;
import framework.services.session.IUserSessionManagerPlugin;
import framework.utils.FilterConfig;
import framework.utils.IColumnFormatter;
import framework.utils.Menu.ClickableMenuItem;
import framework.utils.Menu.HeaderMenuItem;
import framework.utils.Msg;
import framework.utils.Pagination;
import framework.utils.SideBar;
import framework.utils.Table;
import framework.utils.Table.ColumnDef.SorterType;
import framework.utils.Utilities;
import models.framework_models.plugin.PluginConfiguration;
import models.framework_models.plugin.PluginConfigurationBlock;
import models.framework_models.plugin.PluginLog;
import play.Configuration;
import play.Logger;
import play.data.Form;
import play.data.validation.Constraints.Required;
import play.libs.F.Function0;
import play.libs.F.Promise;
import play.mvc.BodyParser;
import play.mvc.Controller;
import play.mvc.Http.MultipartFormData;
import play.mvc.Http.MultipartFormData.FilePart;
import play.mvc.Result;

/**
 * The GUI for managing the plugins.
 * 
 * That is to say :
 * <ul>
 * <li>Displaying the running plugins</li>
 * <li>Creating a new plugin instance</li>
 * <li>Starting/stopping a plugin instance</li>
 * <li>Displaying the log</li>
 * <li>Flushing the logs</li>
 * <li>Changing the plugin configuration</li>
 * </ul>
 * 
 * @author Pierre-Yves Cloux
 */
public class PluginManagerController extends Controller {

    @Inject
    private IUserSessionManagerPlugin userSessionManagerPlugin;
    @Inject
    private IPluginManagerService pluginManagerService;
    @Inject
    private IEventBroadcastingService eventBroadcastingService;
    @Inject
    private ISecurityService securityService;
    @Inject
    private II18nMessagesPlugin i18nMessagesPlugin;
    @Inject
    private Configuration configuration;
    @Inject
    private IPreferenceManagerPlugin preferencManagerPlugin;

    private static Logger.ALogger log = Logger.of(PluginManagerController.class);
    public static final int MAX_CONFIG_FILE_SIZE = 1 * 1024 * 1024;

    /**
     * Form managing the registration of a plugin.
     */
    public static Form<PluginRegistrationFormObject> registrationFormTemplate = Form
            .form(PluginRegistrationFormObject.class);

    /**
     * Form managing the edition of a configuration block.
     */
    public static Form<PluginConfigurationBlockObject> pluginConfigurationBlockFormTemplate = Form
            .form(PluginConfigurationBlockObject.class);

    /**
     * Table displaying the various available configuration blocks for a plugin.
     */
    public static Table<PluginConfigurationBlockObject> configurationBlocksTableTemplate = new Table<PluginConfigurationBlockObject>() {
        {

            this.addColumn("name", "pluginConfigurationBlockIdentifier",
                    "object.plugin_configuration_block.name.label", SorterType.NONE);
            this.setJavaColumnFormatter("name", new IColumnFormatter<PluginConfigurationBlockObject>() {
                @Override
                public String apply(PluginConfigurationBlockObject object, Object value) {
                    return Msg.get(object.getPluginConfigurationBlockDescriptor().getName());
                }
            });

            this.addColumn("description", "pluginConfigurationBlockIdentifier",
                    "object.plugin_configuration_block.description.label", SorterType.NONE);
            this.setJavaColumnFormatter("description", new IColumnFormatter<PluginConfigurationBlockObject>() {
                @Override
                public String apply(PluginConfigurationBlockObject object, Object value) {
                    return Msg.get(object.getPluginConfigurationBlockDescriptor().getDescription());
                }
            });

            this.addColumn("type", "pluginConfigurationBlockIdentifier", "", SorterType.NONE);
            this.setJavaColumnFormatter("type", new IColumnFormatter<PluginConfigurationBlockObject>() {
                @Override
                public String apply(PluginConfigurationBlockObject object, Object value) {
                    switch (object.getPluginConfigurationBlockDescriptor().getEditionType()) {
                    case FILE:
                        return IMafConstants.BOOTSTRAP_FILETYPE_BINARY;
                    case XML:
                        return IMafConstants.BOOTSTRAP_FILETYPE_XML;
                    case PROPERTIES:
                        return IMafConstants.BOOTSTRAP_FILETYPE_PROPERTIES;
                    case JAVASCRIPT:
                        return IMafConstants.BOOTSTRAP_FILETYPE_JAVASCRIPT;
                    default:
                        return IMafConstants.BOOTSTRAP_FILETYPE_TXT;
                    }
                }
            });
            setColumnCssClass("type", IMafConstants.BOOTSTRAP_COLUMN_1);
            setColumnValueCssClass("type", IMafConstants.BOOTSTRAP_TEXT_ALIGN_RIGHT);

            this.setLineAction(new IColumnFormatter<PluginConfigurationBlockObject>() {
                @Override
                public String apply(PluginConfigurationBlockObject object, Object value) {
                    return routes.PluginManagerController.editConfigurationBlock(object.pluginConfigurationId,
                            object.getPluginConfigurationBlockDescriptor().getIdentifier()).url();
                }
            });

            this.setIdFieldName("pluginConfigurationBlockIdentifier");
        }
    };

    /**
     * Table displaying the logs for a plugin.
     */
    public static Table<PluginLog> pluginLogsTableTemplate = new Table<PluginLog>() {
        {
            this.addColumn("lastUpdate", "lastUpdate", "object.plugin_log.last_update.label", SorterType.NONE);
            this.addColumn("isError", "isError", "object.plugin_log.is_error.label", SorterType.NONE);
            this.setJavaColumnFormatter("isError", new IColumnFormatter<PluginLog>() {
                @Override
                public String apply(PluginLog object, Object value) {
                    if (object.isError) {
                        return String.format(IMafConstants.LABEL_DANGER_FORMAT, "ERROR");
                    }
                    return String.format(IMafConstants.LABEL_INFO_FORMAT, "INFO");
                }
            });
            this.addColumn("event", "event", "object.plugin_log.event.label", SorterType.NONE);
            this.addColumn("logMessage", "logMessage", "object.plugin_log.log_message.label", SorterType.NONE,
                    false);
            this.addColumn("transactionId", "transactionId", "object.plugin_log.transaction_id.label",
                    SorterType.NONE);
            this.addColumn("dataType", "dataType", "object.plugin_log.data_type.label", SorterType.NONE);
            this.addColumn("internalId", "internalId", "object.plugin_log.internal_id.label", SorterType.NONE);
            this.addColumn("externalId", "externalId", "object.plugin_log.external_id.label", SorterType.NONE);
            this.setIdFieldName("id");
        }
    };

    /**
     * Filter config for the managing the search or order features on logs.
     */
    private static FilterConfig<PluginLog> pluginLogsFilterConfig = new FilterConfig<PluginLog>() {
        {
            addColumnConfiguration("lastUpdate", "lastUpdate", "object.plugin_log.last_update.label",
                    new TextFieldFilterComponent("*"), true, false, SortStatusType.DESC);
            addColumnConfiguration("isError", "isError", "object.plugin_log.is_error.label",
                    new CheckboxFilterComponent(true), true, true, SortStatusType.UNSORTED);
            addColumnConfiguration("event", "event", "object.plugin_log.event.label",
                    new TextFieldFilterComponent("*"), true, false, SortStatusType.UNSORTED);
            addColumnConfiguration("logMessage", "logMessage", "object.plugin_log.log_message.label",
                    new TextFieldFilterComponent("*"), true, false, SortStatusType.UNSORTED);
            addColumnConfiguration("transactionId", "transactionId", "object.plugin_log.transaction_id.label",
                    new TextFieldFilterComponent("*"), false, false, SortStatusType.UNSORTED);
            addColumnConfiguration("dataType", "dataType", "object.plugin_log.data_type.label",
                    new TextFieldFilterComponent("*"), false, false, SortStatusType.UNSORTED);
            addColumnConfiguration("internalId", "internalId", "object.plugin_log.internal_id.label",
                    new TextFieldFilterComponent("*"), false, false, SortStatusType.UNSORTED);
            addColumnConfiguration("externalId", "externalId", "object.plugin_log.external_id.label",
                    new TextFieldFilterComponent("*"), false, false, SortStatusType.UNSORTED);
        }
    };

    /**
     * Table listing the various plugin instances (PluginConfiguration).
     */
    public static Table<PluginConfigurationDescriptionTableObject> pluginConfigurationsTableTemplate;

    static {
        pluginConfigurationsTableTemplate = new Table<PluginManagerController.PluginConfigurationDescriptionTableObject>() {

            {
                this.addColumn("id", "id", "object.plugin_configuration.id.label", SorterType.NONE);
                this.addColumn("name", "name", "object.plugin_configuration.name.label", SorterType.NONE);
                this.addColumn("definitionName", "definitionName", "object.plugin_definition.name.label",
                        SorterType.NONE);
                this.addColumn("definitionVersion", "definitionVersion", "object.plugin_definition.version.label",
                        SorterType.NONE);
                this.addColumn("status", "status", "object.plugin_configuration.status.label", SorterType.NONE);
                this.setJavaColumnFormatter("status",
                        new IColumnFormatter<PluginConfigurationDescriptionTableObject>() {
                            @Override
                            public String apply(PluginConfigurationDescriptionTableObject object, Object value) {
                                return getHtmlFromPluginStatus(object.status);
                            }
                        });
                this.setLineAction(new IColumnFormatter<PluginConfigurationDescriptionTableObject>() {
                    @Override
                    public String apply(PluginConfigurationDescriptionTableObject object, Object value) {
                        return routes.PluginManagerController.pluginConfigurationDetails(object.id).url();
                    }

                });
                this.setIdFieldName("id");
            }
        };
    }

    /**
     * Default constructor.
     */
    public PluginManagerController() {
    }

    /**
     * Distribute the plugin image.
     * 
     * @param identifier
     *            the image identifier
     * @param isBigImage
     *            true for the big image, esle the small
     */
    @SubjectPresent
    public Promise<Result> image(String identifier, boolean isBigImage) {

        InputStream inStream = null;
        if (isBigImage) {
            inStream = getPluginManagerService().getPluginBigImageSrc(identifier);
        } else {
            inStream = getPluginManagerService().getPluginSmallImageSrc(identifier);
        }
        if (inStream == null) {
            if (log.isDebugEnabled()) {
                log.debug("Image [" + (isBigImage ? "BIG" : "SMALL") + "] not found for plugin " + identifier);
            }
            return Promise.promise(() -> notFound());
        }
        if (log.isDebugEnabled()) {
            log.debug("Image [" + (isBigImage ? "BIG" : "SMALL") + "] was found for plugin " + identifier);
        }
        final InputStream finalInStream = inStream;
        return Promise.promise(() -> ok(finalInStream));
    }

    /**
     * Display the overview display for the plugins.<br/>
     * It consists in a list of plugins with their status (started/stopped).
     * 
     * @throws AccountManagementException
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION), @Group(IMafConstants.API_MANAGER_PERMISSION),
            @Group(IMafConstants.PARTNER_SYNDICATION_PERMISSION) })
    public Result index() throws AccountManagementException {

        if (!getSecurityService().restrict(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION)) {
            if (getSecurityService().restrict(IMafConstants.API_MANAGER_PERMISSION)) {
                return redirect(controllers.admin.routes.ApiManagerController.index());
            } else {
                return redirect(controllers.admin.routes.DataSyndicationController.viewMasterAgreements());
            }
        }

        List<PluginConfigurationDescriptionTableObject> pluginConfigurations = new ArrayList<PluginConfigurationDescriptionTableObject>();

        Map<Long, IPluginInfo> registeredPlugins = getPluginManagerService().getRegisteredPluginDescriptors();
        for (Long pluginConfigurationId : registeredPlugins.keySet()) {
            PluginConfigurationDescriptionTableObject tableObject = new PluginConfigurationDescriptionTableObject();
            tableObject.id = pluginConfigurationId;
            IPluginInfo pluginInfo = registeredPlugins.get(pluginConfigurationId);
            tableObject.name = pluginInfo.getPluginConfigurationName();
            tableObject.definitionIdentifier = pluginInfo.getDescriptor().getIdentifier();
            tableObject.definitionName = Msg.get(pluginInfo.getDescriptor().getName());
            tableObject.definitionDescription = Msg.get(pluginInfo.getDescriptor().getDescription());
            tableObject.definitionProviderUrl = pluginInfo.getDescriptor().getVendorUrl();
            tableObject.definitionVersion = pluginInfo.getDescriptor().getVersion();
            tableObject.status = pluginInfo.getPluginStatus();
            pluginConfigurations.add(tableObject);
        }
        return ok(views.html.admin.plugin.pluginmanager_index
                .render(pluginConfigurationsTableTemplate.fill(pluginConfigurations)));
    }

    /**
     * Display the screen which allow to create or delete a plugin
     * configuration.<br/>
     * Which is in the {@link IPluginManagerService} consists in a registration
     * at non registration.
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result registration() {

        // Identify already registered plugin definitions
        Set<String> registeredDefinitions = getAlreadyRegisteredPlugins(getPluginManagerService());

        // Select the definitions to be displayed (remove the mono instance
        // plugins which are already registered)
        Map<String, Pair<Boolean, IPluginDescriptor>> pluginDescriptors = getPluginManagerService()
                .getAllPluginDescriptors();
        List<Pair<Boolean, IPluginDescriptor>> plugins = new ArrayList<>();
        for (String key : pluginDescriptors.keySet()) {
            Pair<Boolean, IPluginDescriptor> record = pluginDescriptors.get(key);
            IPluginDescriptor pluginDescriptor = record.getRight();
            if (!(registeredDefinitions.contains(pluginDescriptor.getIdentifier())
                    && !pluginDescriptor.multiInstanceAllowed())) {
                plugins.add(Pair.of(record.getLeft(), pluginDescriptor));
            }
        }

        return ok(views.html.admin.plugin.pluginmanager_registration.render(plugins));
    }

    /**
     * Display the form for registering a plugin (= creating a new plugin
     * configuration and registering it).
     * 
     * @param pluginDefinitionIdentifier
     *            a plugin definition identifier (the template for the plugin)
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result displayRegistrationForm(String pluginDefinitionIdentifier) {
        PluginRegistrationFormObject pluginRegistrationFormObject = new PluginRegistrationFormObject();

        IPluginDescriptor pluginRunnerDescriptor = getPluginManagerService()
                .getAvailablePluginDescriptor(pluginDefinitionIdentifier);
        if (pluginRunnerDescriptor == null || (!pluginRunnerDescriptor.multiInstanceAllowed()
                && getAlreadyRegisteredPlugins(getPluginManagerService()).contains(pluginDefinitionIdentifier))) {
            return badRequest();
        }
        String definitionName = Msg.get(pluginRunnerDescriptor.getName());
        pluginRegistrationFormObject.definitionName = definitionName;
        pluginRegistrationFormObject.identifier = pluginDefinitionIdentifier;
        pluginRegistrationFormObject.name = definitionName + " " + String.format("%1$td/%1$tm/%1$tY", new Date());
        return ok(views.html.admin.plugin.pluginmanager_registration_form
                .render(registrationFormTemplate.fill(pluginRegistrationFormObject)));
    }

    /**
     * Create the {@link PluginConfiguration} instance and register the plugin.
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result registerPlugin() {
        Form<PluginRegistrationFormObject> boundForm = registrationFormTemplate.bindFromRequest();
        if (boundForm.hasErrors()) {
            return ok(views.html.admin.plugin.pluginmanager_registration_form.render(boundForm));
        }

        // Create the configuration record
        PluginRegistrationFormObject pluginRegistrationFormObject = boundForm.get();
        try {
            getPluginManagerService().registerPlugin(pluginRegistrationFormObject.name,
                    pluginRegistrationFormObject.identifier);
            Utilities.sendSuccessFlashMessage(
                    Msg.get("admin.plugin_manager.configuration.new.success", pluginRegistrationFormObject.name));
        } catch (PluginException e) {
            Utilities.sendErrorFlashMessage(
                    Msg.get("admin.plugin_manager.configuration.new.error", pluginRegistrationFormObject.name));
            log.error("Failed to register manually the plugin in the manager controller", e);
        }

        return redirect(routes.PluginManagerController.index());
    }

    /**
     * Delete the plugin instance and unregister it from the manager.
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result unregisterPlugin(Long pluginConfigurationId) {
        IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                .get(pluginConfigurationId);
        if (pluginInfo == null) {
            Utilities.sendErrorFlashMessage(
                    Msg.get("admin.plugin_manager.configuration.view.not_exists", pluginConfigurationId));
            return redirect(routes.PluginManagerController.index());
        }
        if (!pluginInfo.getDescriptor().autoRegister()) {
            String pluginConfigurationName = pluginInfo.getPluginConfigurationName();
            try {
                getPluginManagerService().unregisterPlugin(pluginConfigurationId);
                Utilities.sendSuccessFlashMessage(
                        Msg.get("admin.plugin_manager.configuration.delete.success", pluginConfigurationName));
            } catch (Exception e) {
                Utilities.sendErrorFlashMessage(
                        Msg.get("admin.plugin_manager.configuration.delete.error", pluginConfigurationName));
                log.error("Fail to unregister a plugin configuration manually", e);
            }
        }
        return redirect(routes.PluginManagerController.index());
    }

    /**
     * Display the details of the plugin definition.
     * 
     * @param pluginDefinitionIdentifier
     *            a plugin definition identifier
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result pluginDefinitionDetails(String pluginDefinitionIdentifier) {
        IPluginDescriptor pluginDescriptor = getPluginManagerService()
                .getPluginDescriptor(pluginDefinitionIdentifier);
        if (pluginDescriptor == null) {
            Utilities.sendErrorFlashMessage(
                    Msg.get("admin.plugin_manager.definition.view.not_exists", pluginDefinitionIdentifier));
            return redirect(routes.PluginManagerController.registration());
        }
        return ok(views.html.admin.plugin.pluginmanager_definition_details.render(pluginDescriptor,
                getPluginManagerService().isPluginAvailable(pluginDescriptor.getIdentifier())));
    }

    /**
     * Display the detailed view for a plugin.<br/>
     * This detailed view contains the description of the plugin, the status,
     * the type of Data which the plugin is supporting, the logs, the
     * configuration blocks, etc.
     * 
     * @param pluginConfigurationId
     *            the Id of the {@link PluginConfiguration} object
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result pluginConfigurationDetails(Long pluginConfigurationId) {

        IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                .get(pluginConfigurationId);

        if (pluginInfo == null) {
            Utilities.sendErrorFlashMessage(
                    Msg.get("admin.plugin_manager.configuration.view.not_exists", pluginConfigurationId));
            return redirect(routes.PluginManagerController.index());
        }
        // Plugin configuration block
        List<PluginConfigurationBlockObject> configurationBlocks = new ArrayList<PluginConfigurationBlockObject>();
        if (pluginInfo.getDescriptor().getConfigurationBlockDescriptors() != null) {
            for (IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor : pluginInfo.getDescriptor()
                    .getConfigurationBlockDescriptors().values()) {
                configurationBlocks.add(new PluginConfigurationBlockObject(pluginConfigurationBlockDescriptor,
                        pluginConfigurationId));
            }
        }

        // Plugin logs
        String uid = getUserSessionManagerPlugin().getUserSessionId(ctx());
        FilterConfig<PluginLog> filterConfig = pluginLogsFilterConfig.getCurrent(uid, request());

        ExpressionList<PluginLog> pluginLogExpressionList = filterConfig.updateWithSearchExpression(
                PluginLog.getAllPluginLogsForPluginConfigurationId(pluginConfigurationId));
        filterConfig.updateWithSortExpression(pluginLogExpressionList);

        Pagination<PluginLog> pluginLogsPagination = new Pagination<PluginLog>(this.getPreferencManagerPlugin(),
                pluginLogExpressionList);
        pluginLogsPagination.setCurrentPage(filterConfig.getCurrentPage());

        Table<PluginLog> pluginLogsTable = pluginLogsTableTemplate.fill(pluginLogsPagination.getListOfObjects(),
                filterConfig.getColumnsToHide());

        return ok(views.html.admin.plugin.pluginmanager_configuration_details.render(
                pluginInfo.getPluginConfigurationName(), pluginConfigurationId, pluginInfo,
                configurationBlocksTableTemplate.fill(configurationBlocks), pluginLogsPagination, pluginLogsTable,
                filterConfig));
    }

    /**
     * Dynamic management of the table.
     * 
     * @param pluginConfigurationId
     *            a plugin configuration id
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result filterPluginLogs(Long pluginConfigurationId) {

        try {

            String uid = getUserSessionManagerPlugin().getUserSessionId(ctx());
            FilterConfig<PluginLog> filledFilterConfig = pluginLogsFilterConfig.persistCurrentInDefault(uid,
                    request());

            if (filledFilterConfig == null) {
                return ok(views.html.framework_views.parts.table.dynamic_tableview_no_more_compatible.render());
            } else {

                ExpressionList<PluginLog> pluginLogExpressionList = filledFilterConfig.updateWithSearchExpression(
                        PluginLog.getAllPluginLogsForPluginConfigurationId(pluginConfigurationId));

                filledFilterConfig.updateWithSortExpression(pluginLogExpressionList);

                Pagination<PluginLog> pagination = new Pagination<PluginLog>(this.getPreferencManagerPlugin(),
                        pluginLogExpressionList);
                pagination.setCurrentPage(filledFilterConfig.getCurrentPage());

                Table<PluginLog> table = pluginLogsTableTemplate.fill(pagination.getListOfObjects(),
                        filledFilterConfig.getColumnsToHide());

                return ok(views.html.framework_views.parts.table.dynamic_tableview.render(table, pagination));

            }

        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                    getI18nMessagesPlugin());
        }
    }

    /**
     * Start the specified plugin.
     * 
     * @param pluginConfigurationId
     *            the Id of the {@link PluginConfiguration} object
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result startPlugin(Long pluginConfigurationId) {
        IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                .get(pluginConfigurationId);
        if (pluginInfo == null) {
            return badRequest();
        }
        if (!pluginInfo.getDescriptor().autoRegister()) {
            String pluginConfigurationName = pluginInfo.getPluginConfigurationName();
            try {
                getPluginManagerService().startPlugin(pluginConfigurationId);
                Utilities.sendSuccessFlashMessage(
                        Msg.get("admin.plugin_manager.configuration.view.panel.admin.start.success",
                                Msg.get(pluginConfigurationName)));
            } catch (PluginException e) {
                log.error("Exception while attempting to start the plugin " + pluginConfigurationId, e);
                Utilities.sendErrorFlashMessage(
                        Msg.get("admin.plugin_manager.configuration.view.panel.admin.start.error",
                                Msg.get(pluginConfigurationName)));
            }
        }
        return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
    }

    /**
     * Stop the specified plugin.
     * 
     * @param pluginConfigurationId
     *            the Id of the {@link PluginConfiguration} object
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result stopPlugin(Long pluginConfigurationId) {
        IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                .get(pluginConfigurationId);
        if (pluginInfo == null) {
            return badRequest();
        } else {
            if (!pluginInfo.getDescriptor().autoRegister()) {
                String pluginConfigurationName = pluginInfo.getPluginConfigurationName();
                getPluginManagerService().stopPlugin(pluginConfigurationId);
                Utilities.sendSuccessFlashMessage(
                        Msg.get("admin.plugin_manager.configuration.view.panel.admin.stop.success",
                                Msg.get(pluginConfigurationName)));
            }
        }
        return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
    }

    /**
     * Flush the logs which are associated with this plugin.
     * 
     * @param pluginConfigurationId
     *            the Id of the {@link PluginConfiguration} object
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result flushLogs(Long pluginConfigurationId) {
        PluginLog.flushPluginLog(pluginConfigurationId);
        Utilities.sendSuccessFlashMessage(
                Msg.get("admin.plugin_manager.configuration.view.panel.log.flush.success"));
        return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
    }

    /**
     * Update the specified {@link PluginConfigurationBlock} for the specified
     * {@link PluginConfiguration}.
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     * @param pluginConfigurationBlockIdentifier
     *            the plugin configuration block identifier
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result editConfigurationBlock(Long pluginConfigurationId, String pluginConfigurationBlockIdentifier) {
        try {
            Pair<IPluginConfigurationBlockDescriptor, byte[]> configBlock = getPluginManagerService()
                    .getPluginConfigurationBlock(pluginConfigurationId, pluginConfigurationBlockIdentifier);
            PluginConfigurationBlockObject pluginConfigurationBlockObject = new PluginConfigurationBlockObject();
            pluginConfigurationBlockObject.value = new String(configBlock.getRight());
            return ok(views.html.admin.plugin.pluginmanager_configblock_edit.render(pluginConfigurationId,
                    configBlock.getLeft(),
                    pluginConfigurationBlockFormTemplate.fill(pluginConfigurationBlockObject),
                    getSyntaxFromConfigurationBlockEditionType(configBlock.getLeft().getEditionType())));
        } catch (PluginException e) {
            log.error("Error while downloading the plugin configuration " + pluginConfigurationBlockIdentifier
                    + " for " + pluginConfigurationId, e);
            return badRequest();
        }
    }

    /**
     * Return the configuration block descriptor associated with the specified
     * plugin id and the specified identifier.
     * 
     * @param pluginConfigurationId
     *            a plugin id
     * @param pluginConfigurationBlockIdentifier
     *            a configration block identifier
     * @return
     */
    private IPluginConfigurationBlockDescriptor getPluginConfigurationBlockDescriptor(Long pluginConfigurationId,
            String pluginConfigurationBlockIdentifier) {
        IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                .get(pluginConfigurationId);
        if (pluginInfo == null || pluginInfo.getDescriptor().getConfigurationBlockDescriptors() == null
                || !pluginInfo.getDescriptor().getConfigurationBlockDescriptors()
                        .containsKey(pluginConfigurationBlockIdentifier)) {
            return null;
        }
        IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor = pluginInfo.getDescriptor()
                .getConfigurationBlockDescriptors().get(pluginConfigurationBlockIdentifier);
        return pluginConfigurationBlockDescriptor;
    }

    /**
     * Return a {@link Syntax} from the
     * {@link IPluginConfigurationBlockDescriptor} edition type.
     * 
     * @param configurationBlockEditionType
     *            an edition type
     * @return a syntax
     */
    private Syntax getSyntaxFromConfigurationBlockEditionType(
            ConfigurationBlockEditionType configurationBlockEditionType) {
        switch (configurationBlockEditionType) {
        case XML:
            return Syntax.XML;
        case PROPERTIES:
            return Syntax.PROPERTIES;
        case JAVASCRIPT:
            return Syntax.JAVASCRIPT;
        case VELOCITY:
            return Syntax.VELOCITY;
        default:
            return null;
        }
    }

    /**
     * Update the specified {@link PluginConfigurationBlock} for the specified
     * {@link PluginConfiguration}.
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     * @param pluginConfigurationBlockIdentifier
     *            the plugin configuration block identifier
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result updateConfigurationBlock(Long pluginConfigurationId, String pluginConfigurationBlockIdentifier) {
        IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor = getPluginConfigurationBlockDescriptor(
                pluginConfigurationId, pluginConfigurationBlockIdentifier);
        if (pluginConfigurationBlockDescriptor == null) {
            return badRequest();
        }

        Form<PluginConfigurationBlockObject> boundForm = pluginConfigurationBlockFormTemplate.bindFromRequest();
        if (boundForm.hasErrors()) {
            return ok(views.html.admin.plugin.pluginmanager_configblock_edit.render(pluginConfigurationId,
                    pluginConfigurationBlockDescriptor, boundForm, getSyntaxFromConfigurationBlockEditionType(
                            pluginConfigurationBlockDescriptor.getEditionType())));
        }

        // Update the configuration block
        PluginConfigurationBlockObject pluginConfigurationBlockObject = boundForm.get();
        try {
            getPluginManagerService().updatePluginConfiguration(pluginConfigurationId,
                    pluginConfigurationBlockIdentifier,
                    (pluginConfigurationBlockObject.value != null ? pluginConfigurationBlockObject.value.getBytes()
                            : null));
            Utilities.sendWarningFlashMessage(Msg.get("admin.plugin_manager.configuration_block.edit.success",
                    Msg.get(pluginConfigurationBlockDescriptor.getName())));
        } catch (PluginException e) {
            return redirect(routes.PluginManagerController.index());
        }
        return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
    }

    /**
     * Export the plugin configuration as an XML file.
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result exportConfiguration(Long pluginConfigurationId) {
        if (log.isDebugEnabled()) {
            log.debug("Export of the configuration blocks for the plugin " + pluginConfigurationId);
        }
        response().setContentType("application/xml");
        response().setHeader("Content-disposition", "attachment; filename=export.xml");
        try {
            String configuration = getPluginManagerService().exportPluginConfiguration(pluginConfigurationId);
            if (log.isDebugEnabled()) {
                log.debug("Found the configuration " + configuration);
            }
            return ok(configuration);
        } catch (PluginException e) {
            Utilities.sendErrorFlashMessage(Msg.get("admin.plugin_manager.configuration_block.export.error"));
            return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
        }
    }

    /**
     * Import a previously exported configuration file.<br/>
     * The file is posted using a file input control.
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     * @return
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    @BodyParser.Of(value = BodyParser.MultipartFormData.class, maxLength = MAX_CONFIG_FILE_SIZE)
    public Promise<Result> importConfiguration(Long pluginConfigurationId) {
        // Perform the upload
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                try {
                    if (log.isDebugEnabled()) {
                        log.debug("Configuration upload requested for " + pluginConfigurationId);
                    }
                    MultipartFormData body = request().body().asMultipartFormData();
                    FilePart filePart = body.getFile("import");
                    if (filePart != null) {
                        if (log.isDebugEnabled()) {
                            log.debug("A file has been found");
                        }
                        String configuration = IOUtils.toString(new FileInputStream(filePart.getFile()));
                        if (log.isDebugEnabled()) {
                            log.debug("Content of the uploaded file is " + configuration);
                        }
                        try {
                            getPluginManagerService().importPluginConfiguration(pluginConfigurationId,
                                    configuration);
                            if (log.isDebugEnabled()) {
                                log.debug("Plugin configuration uploaded");
                            }
                            Utilities.sendWarningFlashMessage(Msg.get(
                                    "admin.plugin_manager.configuration.view.panel.configuration.import.success"));
                        } catch (PluginException e) {
                            log.error("Attempt to upload an invalid plugin configuration for "
                                    + pluginConfigurationId, e);
                            Utilities.sendErrorFlashMessage(
                                    Msg.get("admin.plugin_manager.configuration_block.import.error"));
                        }
                    } else {
                        Utilities.sendErrorFlashMessage(Msg.get("form.input.file_field.no_file"));
                    }
                } catch (Exception e) {
                    Utilities.sendErrorFlashMessage(Msg.get("admin.shared_storage.upload.file.size.invalid",
                            FileUtils.byteCountToDisplaySize(MAX_CONFIG_FILE_SIZE)));
                    String message = String.format("Failure while uploading the plugin configuration for %d",
                            pluginConfigurationId);
                    log.error(message);
                    throw new IOException(message, e);
                }
                return redirect(routes.PluginManagerController.pluginConfigurationDetails(pluginConfigurationId));
            }
        });
    }

    /**
     * Return the default value for the plugin configuation block as "text".
     * <br/>
     * Here are the parameters for this method:
     * <ul>
     * <li>pluginConfigurationId : the plugin configuration id</li>
     * <li>pluginConfigurationBlockIdentifier : the identifier of the plugin
     * configuration block</li>
     * </ul>
     * 
     * @param pluginConfigurationId
     *            the plugin configuration id
     * @param pluginConfigurationBlockIdentifier
     *            the plugin configuration block identifier
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result getDefaultConfigurationBlockValue(Long pluginConfigurationId,
            String pluginConfigurationBlockIdentifier) {
        IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor = getPluginConfigurationBlockDescriptor(
                pluginConfigurationId, pluginConfigurationBlockIdentifier);
        if (pluginConfigurationBlockDescriptor == null) {
            return badRequest();
        }
        return ok(new String(pluginConfigurationBlockDescriptor.getDefaultValue()));
    }

    /**
     * Post an admin action to the plugin.
     * 
     * @param pluginConfigurationId
     *            a plugin
     * @param pluginActionIdentifier
     *            the action identifier
     */
    @Restrict({ @Group(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION) })
    public Result postAdminActionToPlugin(Long pluginConfigurationId, String pluginActionIdentifier) {
        try {

            IPluginInfo pluginInfo = getPluginManagerService().getRegisteredPluginDescriptors()
                    .get(pluginConfigurationId);
            if (pluginInfo == null || pluginInfo.getActionDescriptors() == null
                    || !pluginInfo.getActionDescriptors().containsKey(pluginActionIdentifier)) {
                return badRequest();
            }
            IPluginActionDescriptor pluginActionDescriptor = pluginInfo.getActionDescriptors()
                    .get(pluginActionIdentifier);
            EventMessage eventMessage = new EventMessage();
            eventMessage.setPluginConfigurationId(pluginConfigurationId);
            eventMessage.setMessageType(MessageType.CUSTOM);
            eventMessage.setDataType(pluginActionDescriptor.getDataType());
            eventMessage.setPayload(pluginActionDescriptor.getPayLoad(null));
            getEventBroadcastingService().postOutMessage(eventMessage);

            log.info(String.format("Admin message for plugin %d posted with transaction id %s",
                    pluginConfigurationId, eventMessage.getTransactionId()));
        } catch (Exception e) {
            log.error(String.format("Error with admin message for plugin %d", pluginConfigurationId), e);
            return badRequest();
        }
        return ok();
    }

    /**
     * Return an HTML representation of the plugin status.
     * 
     * @param pluginStatus
     *            a plugin status
     * @return a String (HTML)
     */
    public static String getHtmlFromPluginStatus(PluginStatus pluginStatus) {
        if (pluginStatus.equals(PluginStatus.STARTED)) {
            return String.format(IMafConstants.LABEL_SUCCESS_FORMAT, PluginStatus.STARTED.name());
        }
        if (pluginStatus.equals(PluginStatus.STARTING) || pluginStatus.equals(PluginStatus.STOPPING)) {
            return String.format(IMafConstants.LABEL_WARNING_FORMAT, pluginStatus.name());
        }
        if (pluginStatus.equals(PluginStatus.START_FAILED)) {
            return String.format(IMafConstants.LABEL_DANGER_FORMAT, PluginStatus.START_FAILED.name());
        }
        if (pluginStatus.equals(PluginStatus.STOPPED)) {
            return String.format(IMafConstants.LABEL_DEFAULT_FORMAT, PluginStatus.STOPPED.name());
        }
        return "";
    }

    /**
     * Get the plugins that are already registered.
     * 
     * @param pluginManagerService
     *            the plugin manager service
     */
    private Set<String> getAlreadyRegisteredPlugins(IPluginManagerService pluginManagerService) {
        Collection<IPluginInfo> pluginInfos = pluginManagerService.getRegisteredPluginDescriptors().values();
        Set<String> registeredDefinitions = new HashSet<String>();
        for (IPluginInfo pluginInfo : pluginInfos) {
            registeredDefinitions.add(pluginInfo.getDescriptor().getIdentifier());
        }
        return registeredDefinitions;
    }

    /**
     * Get the plugin manager service.
     */
    private IPluginManagerService getPluginManagerService() {
        return pluginManagerService;
    }

    /**
     * Get the event broadcasting service.
     */
    private IEventBroadcastingService getEventBroadcastingService() {
        return eventBroadcastingService;
    }

    /**
     * Get the security service.
     */
    private ISecurityService getSecurityService() {
        return securityService;
    }

    /**
     * Get the user session manager service.
     */
    private IUserSessionManagerPlugin getUserSessionManagerPlugin() {
        return userSessionManagerPlugin;
    }

    /**
     * {@link Form} object to be used for editing a plugin configuration block.
     * 
     * @author Pierre-Yves Cloux
     */
    public static class PluginConfigurationBlockObject {
        private IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor;

        /**
         * Default constructor.
         */
        public PluginConfigurationBlockObject() {
            super();
        }

        /**
         * Construct a configuration block object with a block descriptor.
         * 
         * @param pluginConfigurationBlockDescriptor
         *            the plugin configuration block descriptor
         * @param pluginConfigurationId
         *            the plugin configuration id
         */
        public PluginConfigurationBlockObject(
                IPluginConfigurationBlockDescriptor pluginConfigurationBlockDescriptor,
                Long pluginConfigurationId) {
            super();
            this.pluginConfigurationBlockDescriptor = pluginConfigurationBlockDescriptor;
            this.pluginConfigurationBlockIdentifier = pluginConfigurationBlockDescriptor.getIdentifier();
            this.pluginConfigurationId = pluginConfigurationId;
        }

        public Long pluginConfigurationId;
        public String pluginConfigurationBlockIdentifier;
        public String value;

        /**
         * Get the plugin configuration block descriptor.
         */
        public IPluginConfigurationBlockDescriptor getPluginConfigurationBlockDescriptor() {
            return pluginConfigurationBlockDescriptor;
        }
    }

    /**
     * {@link Form} object to be used for creating a new
     * {@link PluginConfiguration} object and registering the plugin.
     * 
     * @author Pierre-Yves Cloux
     */
    public static class PluginRegistrationFormObject {
        public String definitionName;
        public String identifier;
        @Required
        public String name;
    }

    /**
     * {@link Table} object to be used for displaying a list of plugin object
     * definitions.
     * 
     * @author Pierre-Yves Cloux
     */
    public static class PluginDefinitionDescriptionTableObject {
        public String identifier;
        public String name;
        public String description;
        public String providerUrl;
        public String version;
        public boolean isAvailable;
    }

    /**
     * {@link Table} object to be used to display the running plugin
     * configurations.
     * 
     * @author Pierre-Yves Cloux
     */
    public static class PluginConfigurationDescriptionTableObject {
        public Long id;
        public String name;
        public String definitionIdentifier;
        public String definitionName;
        public String definitionDescription;
        public String definitionProviderUrl;
        public String definitionVersion;
        public PluginStatus status;
    }

    /**
     * Construct the integration icons bar depending of the sign-in user
     * permissions.
     * 
     * @param isDataSyndicationActive
     *            true if the data syndication is active (conf)
     * @param currentType
     *            the current menu item type, useful to select the correct item
     */
    public static SideBar getIconsBar(Boolean isDataSyndicationActive, MenuItemType currentType) {

        SideBar sideBar = new SideBar();

        HeaderMenuItem pluginsMenu = new HeaderMenuItem("admin.integration.sidebar.plugins", "fa fa-rss",
                currentType.equals(MenuItemType.PLUGINS));
        pluginsMenu
                .setAuthorizedPermissions(Utilities.getListOfArray(IMafConstants.ADMIN_PLUGIN_MANAGER_PERMISSION));
        sideBar.addMenuItem(pluginsMenu);

        pluginsMenu.addSubMenuItem(new ClickableMenuItem("admin.integration.sidebar.plugins.active_plugins",
                controllers.admin.routes.PluginManagerController.index(), "fa fa-plug", false));

        pluginsMenu.addSubMenuItem(new ClickableMenuItem("admin.integration.sidebar.plugins.available_plugins",
                controllers.admin.routes.PluginManagerController.registration(), "fa fa-shopping-bag", false));

        if (isDataSyndicationActive) {

            HeaderMenuItem dataSyndicationMenu = new HeaderMenuItem("admin.integration.sidebar.data_syndication",
                    "fa fa-share-alt", currentType.equals(MenuItemType.DATA_SYNDICATION));
            dataSyndicationMenu.setAuthorizedPermissions(
                    Utilities.getListOfArray(IMafConstants.PARTNER_SYNDICATION_PERMISSION));
            sideBar.addMenuItem(dataSyndicationMenu);

            dataSyndicationMenu.addSubMenuItem(
                    new ClickableMenuItem("admin.integration.sidebar.data_syndication.master_agreements",
                            controllers.admin.routes.DataSyndicationController.viewMasterAgreements(),
                            "fa fa-sign-out", false));

            dataSyndicationMenu.addSubMenuItem(
                    new ClickableMenuItem("admin.integration.sidebar.data_syndication.consumer_agreements",
                            controllers.admin.routes.DataSyndicationController.viewConsumerAgreements(),
                            "fa fa-sign-in", false));

        }

        HeaderMenuItem apiMenu = new HeaderMenuItem("admin.integration.sidebar.api", "fa fa-exchange",
                currentType.equals(MenuItemType.API));
        apiMenu.setAuthorizedPermissions(Utilities.getListOfArray(IMafConstants.API_MANAGER_PERMISSION));
        sideBar.addMenuItem(apiMenu);

        apiMenu.addSubMenuItem(new ClickableMenuItem("admin.integration.sidebar.api.keys",
                controllers.admin.routes.ApiManagerController.index(), "fa fa-key", false));

        apiMenu.addSubMenuItem(new ClickableMenuItem("admin.integration.sidebar.api.browser",
                controllers.admin.routes.ApiManagerController.displayBrowser(), "fa fa-globe", false));

        ClickableMenuItem sharedStorageMenu = new ClickableMenuItem("admin.integration.sidebar.shared_storage",
                controllers.admin.routes.SharedStorageManagerController.index(), "fa fa-folder",
                currentType.equals(MenuItemType.SHARED_STORAGE));
        sharedStorageMenu.setAuthorizedPermissions(Utilities.getListOfArray(IMafConstants.API_MANAGER_PERMISSION));
        sideBar.addMenuItem(sharedStorageMenu);

        return sideBar;
    }

    /**
     * The menu item type.
     * 
     * @author Johann Kohler
     * 
     */
    public static enum MenuItemType {
        PLUGINS, DATA_SYNDICATION, API, SHARED_STORAGE;
    }

    /**
     * Get the i18n messages service.
     */
    private II18nMessagesPlugin getI18nMessagesPlugin() {
        return i18nMessagesPlugin;
    }

    /**
     * Get the Play configuration service.
     */
    private Configuration getConfiguration() {
        return configuration;
    }

    /**
     * Get the preference manager service.
     */
    private IPreferenceManagerPlugin getPreferencManagerPlugin() {
        return this.preferencManagerPlugin;
    }
}