controllers.admin.AuditableController.java Source code

Java tutorial

Introduction

Here is the source code for controllers.admin.AuditableController.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.File;
import java.io.FilenameFilter;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.inject.Inject;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import be.objectify.deadbolt.java.actions.Group;
import be.objectify.deadbolt.java.actions.Restrict;
import constants.IMafConstants;
import controllers.ControllersUtils;
import framework.commons.DataType;
import framework.services.audit.Auditable;
import framework.services.audit.IAuditLoggerService;
import framework.services.configuration.II18nMessagesPlugin;
import framework.services.notification.INotificationManagerPlugin;
import framework.services.session.IUserSessionManagerPlugin;
import framework.services.storage.IAttachmentManagerPlugin;
import framework.services.storage.IPersonalStoragePlugin;
import framework.services.system.ISysAdminUtils;
import framework.utils.DefaultSelectableValueHolder;
import framework.utils.DefaultSelectableValueHolderCollection;
import framework.utils.ISelectableValueHolder;
import framework.utils.ISelectableValueHolderCollection;
import framework.utils.Msg;
import framework.utils.PickerHandler;
import framework.utils.PickerHandler.Handle;
import framework.utils.Table;
import framework.utils.Table.ColumnDef.SorterType;
import framework.utils.TableExcelRenderer;
import framework.utils.Utilities;
import models.framework_models.account.NotificationCategory;
import models.framework_models.account.NotificationCategory.Code;
import play.Configuration;
import play.Logger;
import play.Play;
import play.data.Form;
import play.i18n.Messages;
import play.libs.F.Function0;
import play.libs.F.Promise;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import scala.concurrent.duration.Duration;

/**
 * The auditable controller.
 * 
 * @author Pierre-Yves Cloux
 */
@Restrict({ @Group(IMafConstants.ADMIN_AUDIT_LOG_PERMISSION) })
public class AuditableController extends Controller {

    @Inject
    private IUserSessionManagerPlugin userSessionManagerPlugin;
    @Inject
    private INotificationManagerPlugin notificationManagerPlugin;
    @Inject
    private IPersonalStoragePlugin personalStoragePlugin;
    @Inject
    private ISysAdminUtils sysAdminUtils;
    @Inject
    private IAuditLoggerService auditLoggerService;
    @Inject
    private II18nMessagesPlugin messagesPlugin;
    @Inject
    private Configuration configuration;
    @Inject
    private IAttachmentManagerPlugin attachmentManagerPlugin;

    // Set to true for activating the search box
    public static final boolean PICKER_OBJECTCLASS_SEARCH = false;

    // Set to true to enable navigation of values
    public static final boolean PICKER_OBJECTCLASS_NAVIGABLE = false;

    private static Logger.ALogger log = Logger.of(AuditableController.class);

    private static Table<Auditable> tableTemplate = new Table<Auditable>() {
        {

            this.addColumn("objectClass", "objectClass", "admin.auditable.object_class.label", SorterType.NONE,
                    true);

            this.addColumn("isAuditable", "isAuditable", "admin.auditable.is_auditable.label", SorterType.NONE,
                    true);

            this.addColumn("editActionLink", "objectClass", "", SorterType.NONE);
            setColumnCssClass("editActionLink", IMafConstants.BOOTSTRAP_COLUMN_1);
            setColumnValueCssClass("editActionLink", IMafConstants.BOOTSTRAP_TEXT_ALIGN_RIGHT);

            this.addColumn("deleteActionLink", "objectClass", "", SorterType.NONE);
            setColumnCssClass("deleteActionLink", IMafConstants.BOOTSTRAP_COLUMN_1);
            setColumnValueCssClass("deleteActionLink", IMafConstants.BOOTSTRAP_TEXT_ALIGN_RIGHT);

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

    private static Form<Auditable> auditableForm = Form.form(Auditable.class);

    /**
     * Return the application logs.
     */
    public Promise<Result> downloadApplicationLog() {
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                try {
                    response().setContentType("text/plain");
                    response().setHeader("Content-disposition", "attachment; filename=application.log");
                    return ok(getAuditLoggerService().getApplicationLog());
                } catch (Exception e) {
                    log.error("Unable to download the application log", e);
                    Utilities.sendErrorFlashMessage(Msg.get("unexpected.error.title"));
                }
                return redirect(routes.AuditableController.listAuditable());
            }
        });
    }

    /**
     * Change the log level to debug.
     */
    public Result switchToDebug() {
        getAuditLoggerService()
                .changeLogLevelToDebug(getConfiguration().getInt("maf.audit.application.log.debug.duration"));
        return redirect(routes.AuditableController.listAuditable());
    }

    /**
     * Display a list of Auditable.
     */
    public Result listAuditable() {
        try {
            Table<Auditable> table = tableTemplate.fill(getAuditLoggerService().getAllActiveAuditable());
            return ok(views.html.admin.audit.auditable_table.render(table,
                    getAuditLoggerService().isLogLevelDebug()));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(), getMessagesPlugin());
        }
    }

    /**
     * Delete the Auditable associated with the specified objectClass.
     * 
     * @param objectClass
     *            an Auditable objectClasst
     */
    public Result deleteAuditable(String objectClass) {
        try {
            getAuditLoggerService().deleteAuditable(objectClass);
            Utilities.sendSuccessFlashMessage(Messages.get("admin.auditable.delete.success"));
            return redirect(routes.AuditableController.listAuditable());
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(), getMessagesPlugin());
        }
    }

    /**
     * Display the edition form for the Auditable associated with the specified
     * objectClass.
     * 
     * @param objectClass
     *            an Auditable objectClass
     */
    public Result editAuditable(String objectClass) {
        try {
            Auditable auditable = getAuditLoggerService().getAuditableFromObjectClass(objectClass);
            if (auditable != null) {
                Form<Auditable> loadedForm = auditableForm.fill(auditable);
                return ok(views.html.admin.audit.auditable_form.render(loadedForm));
            } else {
                Utilities.sendErrorFlashMessage(Msg.get("admin.auditable.manage.not_found"));
                return redirect(routes.AuditableController.listAuditable());
            }
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(), getMessagesPlugin());
        }
    }

    /**
     * Display an edition form for a new Auditable.
     */
    public Result createAuditable() {
        try {
            Auditable auditable = new Auditable();
            Form<Auditable> loadedForm = auditableForm.fill(auditable);
            return ok(views.html.admin.audit.auditable_form.render(loadedForm));
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(), getMessagesPlugin());
        }
    }

    /**
     * Save the Auditable submitted by an edition form.
     */
    public Result saveAuditable() {
        try {
            Form<Auditable> boundForm = auditableForm.bindFromRequest();
            if (boundForm.hasErrors()) {
                return ok(views.html.admin.audit.auditable_form.render(boundForm));
            }
            Auditable auditable = boundForm.get();
            getAuditLoggerService().saveAuditable(auditable);
            Utilities.sendSuccessFlashMessage(Messages.get("admin.auditable.manage.success", auditable));
            return redirect(routes.AuditableController.listAuditable());
        } catch (Exception e) {
            return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(), getMessagesPlugin());
        }
    }

    /**
     * Generate an excel representation of all the Auditable objects.
     */
    public Promise<Result> excelAuditable() {
        final List<Auditable> listOfauditable = getAuditLoggerService().getAllActiveAuditable();
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                try {
                    Table<Auditable> excelTableTemplate = new Table<Auditable>() {
                        {
                            this.addColumn("objectClass", "objectClass", "admin.auditable.object_class.label",
                                    SorterType.NONE, true);
                            this.addColumn("isAuditable", "isAuditable", "admin.auditable.is_auditable.label",
                                    SorterType.NONE, true);
                            this.setIdFieldName("objectClass");
                        }
                    };
                    Table<Auditable> exportTable = excelTableTemplate.fill(listOfauditable);
                    byte[] excelFile = TableExcelRenderer.renderNotFormatted(exportTable);
                    response().setContentType("application/x-download");
                    response().setHeader("Content-disposition", "attachment; filename=export.xlsx");
                    return ok(excelFile);
                } catch (Exception e) {
                    return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                            getMessagesPlugin());
                }
            }
        });
    }

    /**
     * Creates an archive of all the audit logs files and set it into the
     * personal space of the current user.
     */
    public Promise<Result> exportAuditLogs() {
        return Promise.promise(new Function0<Result>() {
            @Override
            public Result apply() throws Throwable {
                try {
                    final String currentUserId = getUserSessionManagerPlugin().getUserSessionId(ctx());
                    final String errorMessage = Msg.get("admin.auditable.export.notification.error.message");
                    final String successTitle = Msg.get("admin.auditable.export.notification.success.title");
                    final String successMessage = Msg.get("admin.auditable.export.notification.success.message");
                    final String notFoundMessage = Msg.get("admin.auditable.export.notification.not_found.message");
                    final String errorTitle = Msg.get("admin.auditable.export.notification.error.title");

                    // Audit log export requested
                    if (log.isDebugEnabled()) {
                        log.debug("Audit log export : Audit log export requested by " + currentUserId);
                    }

                    // Execute asynchronously
                    getSysAdminUtils().scheduleOnce(false, "AUDITABLE", Duration.create(0, TimeUnit.MILLISECONDS),
                            new Runnable() {
                                @Override
                                public void run() {
                                    // Find the files to be archived
                                    String logFilesPathAsString = getConfiguration()
                                            .getString("maf.audit.log.location");
                                    File logFilesPath = new File(logFilesPathAsString);
                                    File logDirectory = logFilesPath.getParentFile();

                                    if (!logDirectory.exists()) {
                                        log.error("Log directory " + logDirectory.getAbsolutePath()
                                                + " is not found, please check the configuration");
                                        return;
                                    }

                                    final String logFilePrefix = (logFilesPath.getName().indexOf('.') != -1
                                            ? logFilesPath.getName().substring(0,
                                                    logFilesPath.getName().indexOf('.'))
                                            : logFilesPath.getName());

                                    if (log.isDebugEnabled()) {
                                        log.debug("Audit log export : Selecting files to archive");
                                    }

                                    File[] filesToArchive = logDirectory.listFiles(new FilenameFilter() {
                                        @Override
                                        public boolean accept(File dir, String name) {
                                            return name.startsWith(logFilePrefix);
                                        }
                                    });

                                    if (filesToArchive.length != 0) {
                                        if (log.isDebugEnabled()) {
                                            log.debug("Audit log export : zipping the " + filesToArchive.length
                                                    + " archive files");
                                        }

                                        // Write to the user personal space
                                        final String fileName = String.format(
                                                "auditExport_%1$td_%1$tm_%1$ty_%1$tH-%1$tM-%1$tS.zip", new Date());
                                        ZipOutputStream out = null;
                                        try {
                                            OutputStream personalOut = getPersonalStoragePlugin()
                                                    .createNewFile(currentUserId, fileName);
                                            out = new ZipOutputStream(personalOut);
                                            for (File fileToArchive : filesToArchive) {
                                                ZipEntry e = new ZipEntry(fileToArchive.getName());
                                                out.putNextEntry(e);
                                                byte[] data = FileUtils.readFileToByteArray(fileToArchive);
                                                out.write(data, 0, data.length);
                                                out.closeEntry();
                                            }
                                            getNotificationManagerPlugin().sendNotification(currentUserId,
                                                    NotificationCategory.getByCode(Code.AUDIT), successTitle,
                                                    successMessage,
                                                    controllers.my.routes.MyPersonalStorage.index().url());

                                        } catch (Exception e) {
                                            log.error("Fail to export the audit archives", e);
                                            getNotificationManagerPlugin().sendNotification(currentUserId,
                                                    NotificationCategory.getByCode(Code.ISSUE), errorTitle,
                                                    errorMessage, controllers.admin.routes.AuditableController
                                                            .listAuditable().url());
                                        } finally {
                                            IOUtils.closeQuietly(out);
                                        }
                                    } else {
                                        log.error("No audit archive found in the folder");
                                        getNotificationManagerPlugin().sendNotification(currentUserId,
                                                NotificationCategory.getByCode(Code.ISSUE), errorTitle,
                                                notFoundMessage,
                                                controllers.admin.routes.AuditableController.listAuditable().url());
                                    }
                                }
                            });

                    return ok(Json.newObject());
                } catch (Exception e) {
                    return ControllersUtils.logAndReturnUnexpectedError(e, log, getConfiguration(),
                            getMessagesPlugin());
                }
            }
        });
    }

    public Result dumpDatabase() {
        Utilities.sendSuccessFlashMessage(Msg.get("admin.auditable.mysql.dump.request.success"));

        // prepare the notification messages
        final String successTitle = Msg.get("admin.auditable.mysql.dump.process.success.title");
        final String successMessage = Msg.get("admin.auditable.mysql.dump.process.success.message");
        final String failureTitle = Msg.get("admin.auditable.mysql.dump.process.failure.title");
        final String failureMessage = Msg.get("admin.auditable.mysql.dump.process.failure.message");

        final String uid = getUserSessionManagerPlugin().getUserSessionId(ctx());

        getSysAdminUtils().scheduleOnce(false, "MySQL Dump", Duration.create(0, TimeUnit.MILLISECONDS),
                new Runnable() {
                    @Override
                    public void run() {

                        INotificationManagerPlugin notificationManagerPlugin = getNotificationManagerPlugin();

                        try {
                            String host = "localhost";
                            String schema = "maf";
                            String port = "3306";
                            // URL pattern :
                            // jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]]
                            // [?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]
                            String[] completeUrl = Play.application().configuration().getString("db.default.url")
                                    .split("//");
                            // if there is informations in the url
                            if (completeUrl.length > 1) {
                                String tempUrl = completeUrl[1];
                                // if many urls, get the first one only
                                if (completeUrl[1].contains(",")) {
                                    tempUrl = completeUrl[1].split(",")[0];
                                }
                                // if there is a port
                                if (tempUrl.contains(":")) {
                                    host = tempUrl.split(":")[0];
                                    String portAndSchema = tempUrl.split(":")[1];
                                    // Get the port
                                    if (!tempUrl.contains("/")) {
                                        port = portAndSchema;
                                    } else {
                                        port = portAndSchema.split("/")[0];
                                        schema = portAndSchema.split("/")[1];
                                        if (schema.contains("?")) {
                                            schema = schema.split("\\?")[0];
                                        }
                                    }
                                    // if schema without port
                                } else if (tempUrl.contains("/")) {
                                    host = tempUrl.split("/")[0];
                                    schema = tempUrl.split("/")[1];
                                    // if no schema and no port
                                } else {
                                    host = tempUrl;
                                }
                                // properties are not considered for the dump
                                if (schema.contains("?")) {
                                    schema = schema.split("\\?")[0];
                                }
                            }

                            String user = Play.application().configuration().getString("db.default.username");
                            String passwd = Play.application().configuration().getString("db.default.password");
                            String path = Play.application().configuration().getString("maf.sftp.store.root");
                            String cmd = "mysqldump -R -h " + host + " -P " + port + " -u " + user + " -p" + passwd
                                    + " " + schema + " | gzip > " + path
                                    + "/outputs/maf_`date +'%Y-%m-%d-%H-%M-%S'`.gz";
                            Process p = Runtime.getRuntime().exec(new String[] { "sh", "-c", cmd });
                            log.debug(IOUtils.toString(p.getInputStream(), "UTF-8"));
                            log.debug(IOUtils.toString(p.getErrorStream(), "UTF-8"));
                            p.waitFor();
                            notificationManagerPlugin.sendNotification(uid,
                                    NotificationCategory.getByCode(Code.INFORMATION), successTitle, successMessage,
                                    controllers.admin.routes.SharedStorageManagerController.index().url());
                        } catch (Exception e) {
                            log.error(e.getMessage());
                            notificationManagerPlugin.sendNotification(uid,
                                    NotificationCategory.getByCode(Code.ISSUE), failureTitle, failureMessage,
                                    controllers.admin.routes.AuditableController.listAuditable().url());
                        }
                    }
                });

        return redirect(routes.AuditableController.listAuditable());
    }

    /*--------------------------------------------------------------------------------
     objectClass                                   
     --------------------------------------------------------------------------------*/

    /**
     * Return a JSON representation of the picker values.
     */
    public Result singleValuePickerObjectClassValues() {
        PickerHandler<String> singleValuePickerObjectClass = new PickerHandler<String>(
                this.getAttachmentManagerPlugin(), String.class, new Handle<String>() {
                    @Override
                    public ISelectableValueHolderCollection<String> getInitialValueHolders(List<String> values,
                            Map<String, String> context) {
                        return AuditableController.getSelectableValuesListForObjectClass(
                                context.get("currentObjectClass"), getAuditLoggerService());
                    }
                });
        return singleValuePickerObjectClass.handle(request());
    }

    /**
     * Return the values which can be "selected" in the picker for the field
     * objectClass.
     * 
     * @param currentObjectClass
     *            the class name of the current object
     * @param auditLoggerService
     *            the audit logger service
     */
    private static ISelectableValueHolderCollection<String> getSelectableValuesListForObjectClass(
            String currentObjectClass, IAuditLoggerService auditLoggerService) {
        // Get all the selectable entities from the configuration
        ISelectableValueHolderCollection<String> selectableObjects = new DefaultSelectableValueHolderCollection<String>();
        for (DataType dataType : DataType.getAllAuditableDataTypes()) {
            selectableObjects.add(new DefaultSelectableValueHolder<String>(dataType.getDataTypeClassName(),
                    Msg.get(dataType.getLabel())));
        }
        // Remove the previously selected entities
        List<Auditable> auditables = auditLoggerService.getAllActiveAuditable();
        for (Auditable auditable : auditables) {
            selectableObjects.remove(auditable.objectClass);
        }
        // Add the current value if any (so that it could be displayed and
        // selected again)
        if (!StringUtils.isBlank(currentObjectClass)) {
            selectableObjects.add(new DefaultSelectableValueHolder<String>(currentObjectClass,
                    Msg.get(DataType.getDataTypeFromClassName(currentObjectClass).getLabel())));
        }
        return selectableObjects;
    }

    /**
     * Returns the value which is selected for the specified field.
     * 
     * @param object
     *            the auditable object
     */
    public static ISelectableValueHolder<String> getSelectedValueForObjectClass(Auditable object) {
        return new DefaultSelectableValueHolder<String>(object.objectClass,
                Msg.get(DataType.getDataTypeFromClassName(object.objectClass).getLabel()));
    }

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

    /**
     * Get the notification manager service.
     */
    private INotificationManagerPlugin getNotificationManagerPlugin() {
        return notificationManagerPlugin;
    }

    /**
     * Get the personal storage service.
     */
    private IPersonalStoragePlugin getPersonalStoragePlugin() {
        return personalStoragePlugin;
    }

    /**
     * Get the system admin utils.
     */
    private ISysAdminUtils getSysAdminUtils() {
        return sysAdminUtils;
    }

    /**
     * Get the audit logger service.
     */
    private IAuditLoggerService getAuditLoggerService() {
        return auditLoggerService;
    }

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

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

    /**
     * Get the attachment manager plugin.
     */
    private IAttachmentManagerPlugin getAttachmentManagerPlugin() {
        return this.attachmentManagerPlugin;
    }
}