fr.paris.lutece.plugins.updater.service.PluginManagerService.java Source code

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.plugins.updater.service.PluginManagerService.java

Source

/*
 * Copyright (c) 2002-2014, Mairie de Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.plugins.updater.service;

import fr.paris.lutece.plugins.updater.business.resource.DirectoryTreeResource;
import fr.paris.lutece.plugins.updater.business.resource.FilePatternResource;
import fr.paris.lutece.plugins.updater.business.resource.FileResource;
import fr.paris.lutece.plugins.updater.business.resource.FileSystemResource;
import fr.paris.lutece.plugins.updater.util.sql.SqlUtils;
import fr.paris.lutece.util.filesystem.FileNameComparator;

import org.apache.commons.io.FileUtils;

import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;

import java.sql.SQLException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;

/**
 * Plugin Manager Service
 */
public final class PluginManagerService {
    private static final int FILE = 1;
    private static final int DIRECTORY_TREE = 2;
    private static final int FILE_PREFIX = 3;
    private static final String PATH_BACKUP = "/plugins/updater/backup/";
    private static final String PATH_DEPLOY = "/plugins/updater/deploy/";
    private static final String FOLDER_WEBAPP = "/webapp";
    private static final String FOLDER_SQL = "/sql";
    private static PluginManagerService _singleton = new PluginManagerService();
    private static String _strWebAppPath;
    private static Logger _logger = Logger.getLogger("lutece.plugins.updater");

    /**
     * Private constructor
     */
    private PluginManagerService() {
    }

    /**
     * Returns the unique instance of the service
     * @return The unique instance of the service
     */
    public static PluginManagerService getInstance() {
        return _singleton;
    }

    /**
     * Returns the WebAppPath
     *
     * @return The WebAppPath
     */
    public String getWebAppPath() {
        // For unit testing _strWebAppPath can be initialized manualy without 
        // using AppPathService.getWebAppPath()
        return _strWebAppPath;
    }

    /**
     * Sets the WebAppPath
     *
     * @param strWebAppPath The WebAppPath
     */
    public void setWebAppPath(String strWebAppPath) {
        _strWebAppPath = strWebAppPath;
    }

    /**
     * Clean plugins before update
     * @param nDeleteMode The deletion mode
     */
    public void cleanPluginsMarkedForUpdate(int nDeleteMode) {
        _logger.info("Searching for plugin to remove before update ...");

        String strDeployDirectory = getWebAppPath() + PATH_DEPLOY;
        File fDeployDirectory = new File(strDeployDirectory);
        boolean bFound = false;

        if (fDeployDirectory.exists()) {
            File[] files = fDeployDirectory.listFiles();

            if (files.length > 0) {
                for (int i = 0; i < files.length; i++) {
                    File fPluginDirectory = files[i];
                    String strPluginName = fPluginDirectory.getName();
                    _logger.info("An update has been found for plugin : " + strPluginName
                            + ". The installed plugin will be deleted");
                    bFound = true;
                    cleanPlugin(strPluginName, nDeleteMode, true);
                }
            }
        }

        if (!bFound) {
            _logger.info("No plugin to removed was found.");
        }
    }

    /**
     * Install new plugins or updates (deploy => webapp)
     */
    public void installPlugins() {
        _logger.info("Searching for plugin to install ...");

        String strDeployDirectory = getWebAppPath() + PATH_DEPLOY;
        File fDeployDirectory = new File(strDeployDirectory);
        boolean bFound = false;

        if (fDeployDirectory.exists()) {
            File[] files = fDeployDirectory.listFiles();

            if (files.length > 0) {
                for (int i = 0; i < files.length; i++) {
                    File fPluginDirectory = files[i];
                    String strPluginName = fPluginDirectory.getName();

                    try {
                        _logger.info("Installation data has been found for plugin : " + strPluginName);
                        installPlugin(strPluginName);
                        bFound = true;
                        _logger.info("Plugin '" + strPluginName + "' installed");
                    } catch (UpdaterInstallException ex) {
                        _logger.info("Plugin '" + strPluginName + "'  installation failed : " + ex.getMessage());
                        _logger.info("Plugin '" + strPluginName + "'  installation rolled back");
                        cleanPlugin(strPluginName, 0, false);
                    }
                }
            }
        }

        if (!bFound) {
            _logger.info("No plugin to install or update was found.");
        }
    }

    /**
     * Clean an installed version of a plugin in the webapp. The process backup
     * and remove the current version of the plugin.
     * @param strPluginName The plugin name
     * @param nDeleteMode The deletion mode
     * @param bBackup Backup before cleaning
     */
    public void cleanPlugin(String strPluginName, int nDeleteMode, boolean bBackup) {
        if (bBackup) {
            // backup the plugin
            backupPlugin(strPluginName);
        }

        try {
            // remove it from the webapp
            removePlugin(strPluginName, nDeleteMode);
        } catch (Exception e) {
            restorePlugin(strPluginName);
        }
    }

    /**
     * Remove a plugin from the webapp
     * @param strPluginName The plugin name
     * @param nDeleteMode The deletion mode
     */
    public void removePlugin(String strPluginName, int nDeleteMode) {
        _logger.info("remove plugin : " + strPluginName);

        List<FileSystemResource> listResources = getPluginResources(strPluginName);

        try {
            for (FileSystemResource resource : listResources) {
                resource.delete(nDeleteMode);
                _logger.debug("resource removed : " + resource.getFullPath());
            }
        } catch (IOException ex) {
            _logger.error("error removing plugin : ", ex);
        }
    }

    /**
     * Backup the plugin
     * @param strPluginName The plugin name
     */
    public void backupPlugin(String strPluginName) {
        _logger.info("backup plugin : " + strPluginName);

        List<FileSystemResource> listResources = getPluginResources(strPluginName);
        String strBackupPath = PATH_BACKUP + strPluginName + FOLDER_WEBAPP;

        try {
            for (FileSystemResource resource : listResources) {
                _logger.debug("resource copied : " + resource.getFullPath());
                resource.copy(strBackupPath);
            }
        } catch (IOException ex) {
            _logger.error("error backup plugin : ", ex);
        }
    }

    /**
     * Restore a plugin (backup => webapp)
     * @param strPluginName The plugin name
     */
    public void restorePlugin(String strPluginName) {
        _logger.info("restore plugin : " + strPluginName);

        try {
            File fileSource = new File(getWebAppPath() + PATH_BACKUP + strPluginName + FOLDER_WEBAPP);
            File fileDest = new File(getWebAppPath());

            FileUtils.copyDirectory(fileSource, fileDest);
        } catch (IOException ex) {
            _logger.error("error restoring plugin : ", ex);
        }
    }

    /**
     * Checks if a backup is available for a restore process
     * @param strPluginName The plugin name
     * @return true if the plugin can be restored
     */
    public boolean checkRestorable(String strPluginName) {
        String strBackupDirectory = getWebAppPath() + PATH_BACKUP + strPluginName;
        File fileBackupDirectory = new File(strBackupDirectory);

        return fileBackupDirectory.exists();
    }

    /**
     * Checks if the installation is in progress
     * @param strPluginName The plugin name
     * @return true if the installation is in progress
     */
    public boolean checkInstallInProgress(String strPluginName) {
        String strDeployDirectory = getWebAppPath() + PATH_DEPLOY + strPluginName;
        File fileDeployDirectory = new File(strDeployDirectory);

        return fileDeployDirectory.exists();
    }

    /**
     * Cancel an installation
     * @param strPluginName The plugin name
     */
    public void cancelInstallInProgress(String strPluginName) {
        // Cancel is performed by removing the plugin directory in the DEPLOY directory
        String strDeployDirectory = getWebAppPath() + PATH_DEPLOY + strPluginName;
        File fileDeployDirectory = new File(strDeployDirectory);

        if (fileDeployDirectory.exists()) {
            try {
                FileUtils.deleteDirectory(fileDeployDirectory);
            } catch (IOException ex) {
                _logger.error("error installing plugin : ", ex);
            }
        }
    }

    /**
     * Return the PATH to deploy webapp files of a plugin
     * @param strPluginName The plugin
     * @return The path
     */
    public String getDeployWebappPath(String strPluginName) {
        return getWebAppPath() + PATH_DEPLOY + strPluginName + FOLDER_WEBAPP;
    }

    /**
     * Return the PATH to deploy webapp files of a plugin
     * @param strPluginName The plugin
     * @return The path
     */
    public String getDeploySqlPath(String strPluginName) {
        return getWebAppPath() + PATH_DEPLOY + strPluginName + FOLDER_SQL;
    }

    /**
     * Runs SQL scripts
     * @param strScriptsDirectory The scripts directory
     * @throws UpdaterScriptException If a script exception occurs
     */
    private void executeScripts(String strScriptsDirectory) throws UpdaterScriptException {
        File fileScriptsDirectory = new File(strScriptsDirectory);

        if (fileScriptsDirectory.exists()) {
            try {
                // Use a treeset to order files with a comparator
                TreeSet<File> set = new TreeSet<File>(new FileNameComparator());

                File[] files = fileScriptsDirectory.listFiles();

                if (files != null) {
                    set.addAll(Arrays.asList(files));

                    for (File file : set) {
                        SqlUtils.executeSqlFileScript(file.getAbsolutePath(), null);
                    }
                }
            } catch (IOException e) {
                throw new UpdaterScriptException("file not found", e);
            } catch (SQLException e) {
                throw new UpdaterScriptException("SQL Error", e);
            }
        }
    }

    /**
     * Gives all files of a plugin
     * @param strPluginName The plugin name
     * @return A list of all file resources owned by a plugin
     */
    private List<FileSystemResource> getPluginResources(String strPluginName) {
        List<FileSystemResource> listResources = new ArrayList<FileSystemResource>();
        addResource("/css/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/js/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/images/admin/skin/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/images/local/skin/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/jsp/site/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/jsp/admin/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/WEB-INF/classes/fr/paris/lutece/plugins/" + strPluginName + "/resources", DIRECTORY_TREE,
                listResources);
        addResource("/WEB-INF/conf/plugins/" + strPluginName + ".properties", FILE, listResources);
        addResource("/WEB-INF/conf/plugins/" + strPluginName + "_context.xml", FILE, listResources);
        addResource("/WEB-INF/plugins/" + strPluginName + ".xml", FILE, listResources);
        //        addResource( "/WEB-INF/plugins/" + strPluginName, DIRECTORY_TREE, listResources );   // Plugin's data
        addResource("/WEB-INF/sql/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/WEB-INF/templates/admin/plugins/" + strPluginName, DIRECTORY_TREE, listResources);
        addResource("/WEB-INF/templates/skin/plugins/" + strPluginName, DIRECTORY_TREE, listResources);

        addResource("/WEB-INF/lib/", "plugin-" + strPluginName, FILE_PREFIX, listResources);
        addResource("/WEB-INF/xsl/normal/", "portlet_" + strPluginName, FILE_PREFIX, listResources);

        return listResources;
    }

    /**
     * Add a resource to a list
     * @param strResource The resource to add
     * @param nType The resource type
     * @param listResources The list
     */
    private void addResource(String strResource, int nType, List<FileSystemResource> listResources) {
        addResource(strResource, "", nType, listResources);
    }

    /**
     * Add a resource to a list
     * @param strResourceDirectory The resource's directory
     * @param strResourcePattern The resource's pattern
     * @param nType The resource type
     * @param listResources The list
     */
    private void addResource(String strResourceDirectory, String strResourcePattern, int nType,
            List<FileSystemResource> listResources) {
        try {
            File file = new File(getWebAppPath() + "/" + strResourceDirectory);

            if (file.exists()) {
                FileSystemResource resource = null;

                switch (nType) {
                case FILE:
                    resource = new FileResource(getWebAppPath(), strResourceDirectory);
                    _logger.debug("plugin resource 'file' found : " + file.getPath());

                    break;

                case DIRECTORY_TREE:
                    resource = new DirectoryTreeResource(getWebAppPath(), strResourceDirectory);
                    _logger.debug("plugin resource 'directory tree' found : " + file.getPath());

                    break;

                case FILE_PREFIX:
                    resource = new FilePatternResource(getWebAppPath(), strResourceDirectory, strResourcePattern,
                            FilePatternResource.PATTERN_PREFIX);
                    _logger.debug("plugin resource 'file pattern' found : " + file.getPath());

                    break;

                default:
                    break;
                }

                listResources.add(resource);
            } else {
                _logger.debug("plugin resource not found : " + file.getPath());
            }
        } catch (Exception e) {
            _logger.error("PluginManagerService:AddResource error : " + e.getMessage(), e);
        }
    }

    /**
     * Install a plugin
     * @param strPluginName The plugin name
     * @throws UpdaterInstallException If an install exception occurs
     */
    private void installPlugin(String strPluginName) throws UpdaterInstallException {
        _logger.info("install plugin : " + strPluginName);

        try {
            // Copy webapp's deployment data into the webapp
            File fileSource = new File(getWebAppPath() + PATH_DEPLOY + strPluginName + FOLDER_WEBAPP);
            File fileDest = new File(getWebAppPath());

            FileUtils.copyDirectory(fileSource, fileDest);

            // Execute sql scripts
            executeScripts(getWebAppPath() + PATH_DEPLOY + strPluginName + FOLDER_SQL);

            // If the installation is OK, then delete deployment data
            File fileDeploy = new File(getWebAppPath() + PATH_DEPLOY + strPluginName);
            FileUtils.deleteDirectory(fileDeploy);
        } catch (IOException ex) {
            _logger.error("error installing plugin : ", ex);
            throw new UpdaterInstallException("Installation exception", ex);
        } catch (UpdaterScriptException ex) {
            throw new UpdaterInstallException("Installation exception", ex);
        }
    }
}