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

Java tutorial

Introduction

Here is the source code for fr.paris.lutece.plugins.updater.service.UpdateService.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.version.InvalidVersionException;
import fr.paris.lutece.plugins.updater.business.version.Version;
import fr.paris.lutece.plugins.updater.service.catalog.CatalogInfos;
import fr.paris.lutece.plugins.updater.service.catalog.ICatalogService;
import fr.paris.lutece.plugins.updater.service.catalog.UpgradeInfos;
import fr.paris.lutece.portal.service.init.AppInfo;
import fr.paris.lutece.portal.service.plugin.Plugin;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.util.httpaccess.HttpAccess;
import fr.paris.lutece.util.httpaccess.HttpAccessException;

import org.apache.commons.io.FileUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

/**
 * UpdateService
 */
public class UpdateService implements IUpdateService {
    private static final String PATH_DOWNLOADED = "/plugins/updater/downloaded/";
    private static final String FOLDER_WEBAPP = "/webapp";
    private static final String FOLDER_SQL = "/sql";
    private static final String PLUGIN_NAME = "updater";
    private static final String BEAN_CATALOG_SERVICE = "updater.catalogService";
    private static Version _currentCoreVersion;
    private static int _nStatus;
    private static int _nRegularUpdateCount;
    private static int _nCriticalUpdateCount;

    /**
     * Gets available update for a list of plugins
     * @param listPlugins The list of installed plugins
     * @return Update infos for installed plugins
     */
    @Override
    public List<UpdateInfos> getUpdateInfos(Collection<Plugin> listPlugins) {
        ICatalogService catalogService = (ICatalogService) SpringContextService.getBean(BEAN_CATALOG_SERVICE);
        List<CatalogInfos> listCatalogInfos = catalogService.getCatalogInfos();
        List<UpdateInfos> listUpdatesInfos = new ArrayList<UpdateInfos>();

        if (listPlugins != null) {
            for (Plugin plugin : listPlugins) {
                for (CatalogInfos ci : listCatalogInfos) {
                    if (ci.getPluginName().equals(plugin.getName())) {
                        try {
                            Version vCurrent = Version.parse(plugin.getVersion());
                            Version vRepository = Version.parse(ci.getVersion());

                            // Display only upgrade for plugins that have a more recent version number
                            if (vRepository.compareTo(vCurrent) > 0) {
                                for (UpgradeInfos upgrade : ci.getUpgrades()) {
                                    // Find upgrade corresponding to the current version
                                    if (upgrade.getVersionFrom().equals(plugin.getVersion())
                                            && isCompliantWithCurrentCore(ci)) {
                                        UpdateInfos ui = new UpdateInfos(ci.getPluginName());
                                        ui.setCurrentVersion(plugin.getVersion());
                                        ui.setTargetVersion(ci.getVersion());
                                        ui.setCriticalUpdate(upgrade.getCriticalUpdate());
                                        ui.setDownloaded(checkDownloaded(ci.getPluginName(), ci.getVersion()));
                                        ui.setInstallInProgress(PluginManagerService.getInstance()
                                                .checkInstallInProgress(ci.getPluginName()));
                                        listUpdatesInfos.add(ui);
                                    }
                                }
                            }
                        } catch (InvalidVersionException ex) {
                            AppLogService.error("Invalid version number for plugin : " + ci.getPluginName(), ex);
                        }
                    }
                }
            }
        }

        return listUpdatesInfos;
    }

    /**
     * Gets available plugins not already installed
     * @param listPlugins The list of installed plugins
     * @return New plugins infos list
     */
    @Override
    public List<NewInfos> getNewPluginsInfos(Collection<Plugin> listPlugins) {
        ICatalogService catalogService = (ICatalogService) SpringContextService.getPluginBean(PLUGIN_NAME,
                BEAN_CATALOG_SERVICE);
        List<CatalogInfos> listCatalogInfos = catalogService.getCatalogInfos();
        List<NewInfos> listNewPluginInfos = new ArrayList<NewInfos>();

        for (CatalogInfos ci : listCatalogInfos) {
            if (!isInstalled(ci.getPluginName(), listPlugins) && isCompliantWithCurrentCore(ci)) {
                NewInfos ni = new NewInfos(ci.getPluginName());
                ni.setDescription(ci.getDescription());
                ni.setVersion(ci.getVersion());
                ni.setAuthor(ci.getAuthor());
                ni.setHomepageUrl(ci.getHomepageUrl());
                ni.setDownloaded(checkDownloaded(ci.getPluginName(), ci.getVersion()));
                ni.setInstallInProgress(
                        PluginManagerService.getInstance().checkInstallInProgress(ci.getPluginName()));
                listNewPluginInfos.add(ni);
            }
        }

        return listNewPluginInfos;
    }

    /**
     * Deploy a plugin (downloaded => deploy)
     * @param strPluginName The plugin name
     * @param strVersion The update version
     */
    @Override
    public void deployPlugin(String strPluginName, String strVersion) {
        AppLogService.info("deploy plugin : " + strPluginName);

        try {
            // Copy the webapp folder
            File fileSource = new File(AppPathService.getWebAppPath() + PATH_DOWNLOADED + strPluginName + "/"
                    + strVersion + FOLDER_WEBAPP);
            File fileDest = new File(PluginManagerService.getInstance().getDeployWebappPath(strPluginName));

            if (fileDest.exists()) {
                FileUtils.deleteDirectory(fileDest);
            }

            FileUtils.copyDirectory(fileSource, fileDest);

            // Copy the SQL folder
            fileSource = new File(AppPathService.getWebAppPath() + PATH_DOWNLOADED + strPluginName + "/"
                    + strVersion + FOLDER_SQL);
            fileDest = new File(PluginManagerService.getInstance().getDeploySqlPath(strPluginName));

            if (fileDest.exists()) {
                FileUtils.deleteDirectory(fileDest);
            }

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

    /**
     * Checks if the plugin's catalog info is compliant with the current core version
     * @param ci CatalogInfos
     * @return true if OK, otherwise false
     */
    boolean isCompliantWithCurrentCore(CatalogInfos ci) {
        if (_currentCoreVersion == null) {
            try {
                _currentCoreVersion = Version.parse(AppInfo.getVersion());
            } catch (InvalidVersionException ex) {
                AppLogService.error("Invalid core version ", ex);

                return false;
            }
        }

        try {
            // Checks that the current core version is higher than the min required
            Version requiredMinCoreVersion = Version.parse(ci.getCoreVersionMin());

            if (_currentCoreVersion.compareTo(requiredMinCoreVersion) < 0) {
                return false;
            }

            // Checks that the current core version is lower than the max required
            if (ci.getCoreVersionMax() != null) {
                Version requiredMaxCoreVersion = Version.parse(ci.getCoreVersionMax());

                if (_currentCoreVersion.compareTo(requiredMaxCoreVersion) > 0) {
                    return false;
                }
            }
        } catch (InvalidVersionException ex) {
            AppLogService.error("Invalid version : " + ci.getPluginName(), ex);

            return false;
        }

        return true;
    }

    /**
     * Returns the updater status : (no update available, regular updates
     * available or critical updates available.
     * @return The status
     */
    @Override
    public int getStatus() {
        return _nStatus;
    }

    /**
     * Returns the number of regular updates available
     * @return the number of regular updates available
     */
    @Override
    public int getRegularUpdateCount() {
        return _nRegularUpdateCount;
    }

    /**
     * Returns the number of critical updates available
     * @return the number of critical updates available
     */
    @Override
    public int getCriticalUpdateCount() {
        return _nCriticalUpdateCount;
    }

    /**
     * Check for updates and update the status
     * @param listPlugins The list of installed plugins
     */
    @Override
    public void checkUpdate(Collection<Plugin> listPlugins) {
        _nStatus = STATUS_NO_UPDATE;
        _nRegularUpdateCount = 0;
        _nCriticalUpdateCount = 0;

        List<UpdateInfos> listUpdatesInfos = getUpdateInfos(listPlugins);

        if (listUpdatesInfos.size() > 0) {
            _nStatus = STATUS_REGULAR_UPDATE;

            for (UpdateInfos ui : listUpdatesInfos) {
                if (ui.isCriticalUpdate()) {
                    _nStatus = STATUS_CRITICAL_UPDATE;
                    _nCriticalUpdateCount++;
                } else {
                    _nRegularUpdateCount++;
                }
            }
        }
    }

    /**
     * Tells if a plugin is among of an installed plugin list
     * @param strPluginName The plugin name
     * @param listPlugins The installed plugins list
     * @return true if the plugin is in the list
     */
    private boolean isInstalled(String strPluginName, Collection<Plugin> listPlugins) {
        for (Plugin plugin : listPlugins) {
            if (strPluginName.equals(plugin.getName())) {
                return true;
            }
        }

        return false;
    }

    /**
     * Checks if the release has been downloaded
     * @param strPluginName The plugin name
     * @param strVersion The release version
     * @return true if the release has been downloaded, otherwise false
     */
    private boolean checkDownloaded(String strPluginName, String strVersion) {
        String strReleasesDirectory = AppPathService.getWebAppPath() + PATH_DOWNLOADED + strPluginName + "/"
                + strVersion;
        File fReleasesDirectory = new File(strReleasesDirectory);

        return fReleasesDirectory.exists();
    }

    /**
     * Download a plugin release
     * @param strPluginName The plugin name
     * @param  strVersion The version
     * @throws UpdaterDownloadException If an exception occurs during download
     */
    @Override
    public void downloadPlugin(String strPluginName, String strVersion) throws UpdaterDownloadException {
        CatalogInfos ci = getCatalogInfos(strPluginName);

        if (ci != null) {
            downloadPackage(strPluginName, strVersion, ci.getDownloadUrl());
        }
    }

    /**
     * Download a plugin release
     * @param strPluginName The plugin name
     * @param  strVersion The version
     * @param  strVersionFrom The version from which to upgrade
     * @throws UpdaterDownloadException If an exception occurs during download
    */
    @Override
    public void downloadPluginUpgrade(String strPluginName, String strVersion, String strVersionFrom)
            throws UpdaterDownloadException {
        CatalogInfos ci = getCatalogInfos(strPluginName);

        if (ci != null) {
            for (UpgradeInfos ui : ci.getUpgrades()) {
                if (ui.getVersionFrom().equals(strVersionFrom)) {
                    downloadPackage(strPluginName, strVersion, ui.getDownloadUrl());
                }
            }
        }
    }

    /**
     * Download a package for a given plugin and a given version
     * @param strPluginName The plugin name
     * @param strVersion The version
     * @param strPackageFileUrl The package download url
     * @throws UpdaterDownloadException If an exception occurs during download
     */
    private void downloadPackage(String strPluginName, String strVersion, String strPackageFileUrl)
            throws UpdaterDownloadException {
        try {
            HttpAccess httpAccess = new HttpAccess();
            String strPluginDirectory = AppPathService.getWebAppPath() + PATH_DOWNLOADED + strPluginName;
            File directory = new File(strPluginDirectory);

            if (!directory.exists()) {
                FileUtils.forceMkdir(directory);
            }

            String strPackageFile = strPluginDirectory + "/" + strPluginName + ".zip";
            httpAccess.downloadFile(strPackageFileUrl, strPackageFile);

            String strVersionDirectory = strPluginDirectory + "/" + strVersion;
            extractPackage(strPackageFile, strVersionDirectory);
        } catch (HttpAccessException e) {
            AppLogService.error("Error downloading file : " + e.getMessage(), e);
            throw new UpdaterDownloadException("Error downloading file : " + e.getMessage(), e);
        } catch (IOException e) {
            AppLogService.error("Error creating downloaded file : " + e.getMessage(), e);
            throw new UpdaterDownloadException("Error creating downloaded file : " + e.getMessage(), e);
        }
    }

    /**
     * Extract a package
     * @param strZipFile The package zip file
     * @param strDirectory The target directory
     * @throws UpdaterDownloadException If an exception occurs during download
     */
    private void extractPackage(String strZipFile, String strDirectory) throws UpdaterDownloadException {
        try {
            File file = new File(strZipFile);
            ZipFile zipFile = new ZipFile(file);

            // Each zipped file is indentified by a zip entry :
            Enumeration zipEntries = zipFile.entries();

            while (zipEntries.hasMoreElements()) {
                ZipEntry zipEntry = (ZipEntry) zipEntries.nextElement();

                // Clean the name :
                String strEntryName = zipEntry.getName();

                // The unzipped file :
                File destFile = new File(strDirectory, strEntryName);

                // Create the parent directory structure if needed :
                destFile.getParentFile().mkdirs();

                if (!zipEntry.isDirectory()) {
                    // InputStream from zipped data
                    InputStream inZipStream = null;

                    try {
                        AppLogService.debug("unzipping " + strEntryName + " to " + destFile.getName());
                        inZipStream = zipFile.getInputStream(zipEntry);

                        // OutputStream to the destination file
                        OutputStream outDestStream = new FileOutputStream(destFile);

                        // Helper method to copy data
                        copyStream(inZipStream, outDestStream);

                        inZipStream.close();
                        outDestStream.close();
                    } catch (IOException e) {
                        AppLogService.error("Error extracting file : " + e.getMessage(), e);
                    } finally {
                        try {
                            inZipStream.close();
                        } catch (IOException e) {
                            AppLogService.error("Error extracting file : " + e.getMessage(), e);
                        }
                    }
                } else {
                    AppLogService.debug("skipping directory " + strEntryName);
                }
            }
        } catch (ZipException e) {
            AppLogService.error("Error extracting file : " + e.getMessage(), e);
            throw new UpdaterDownloadException("Error extracting package ", e);
        } catch (IOException e) {
            AppLogService.error("Error extracting file : " + e.getMessage(), e);
        }
    }

    /**
     * Copies data from an input stream to an output stream.
     * @param inStream The input stream
     * @param outStream The output stream
     * @throws IOException If an I/O error occurs
     */
    private static void copyStream(InputStream inStream, OutputStream outStream) throws IOException {
        BufferedInputStream inBufferedStream = new BufferedInputStream(inStream);
        BufferedOutputStream outBufferedStream = new BufferedOutputStream(outStream);

        int nByte;

        while ((nByte = inBufferedStream.read()) > -1) {
            outBufferedStream.write(nByte);
        }

        outBufferedStream.close();
        inBufferedStream.close();
    }

    /**
     * Get catalog infos for a given plugin
     * @param strPluginName The plugin name
     * @return A CatalogInfos object
     */
    private CatalogInfos getCatalogInfos(String strPluginName) {
        ICatalogService catalogService = (ICatalogService) SpringContextService.getPluginBean(PLUGIN_NAME,
                BEAN_CATALOG_SERVICE);
        List<CatalogInfos> listCatalogInfos = catalogService.getCatalogInfos();

        for (CatalogInfos ci : listCatalogInfos) {
            if (ci.getPluginName().equals(strPluginName)) {
                return ci;
            }
        }

        return null;
    }
}