All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.module.CmsModuleImportExportRepository Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 18.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://www.opencms.org
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package org.opencms.module;

import org.opencms.file.CmsObject;
import org.opencms.file.CmsProject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.importexport.CmsImportExportException;
import org.opencms.importexport.CmsImportParameters;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.module.CmsModuleLog.Action;
import org.opencms.report.CmsLogReport;
import org.opencms.report.I_CmsReport;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsStringUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.logging.Log;

import com.google.common.base.Objects;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;

/**
 * Class which manages import/export of modules from repositories configured in opencms-importexport.xml.

*/ public class CmsModuleImportExportRepository { /** Export folder path. */ public static final String EXPORT_FOLDER_PATH = "packages/_export"; /** Import folder path. */ public static final String IMPORT_FOLDER_PATH = "packages/_import"; /** Suffix for module zip files. */ public static final String SUFFIX = ".zip"; /** The log instance for this class. */ private static final Log LOG = CmsLog.getLog(CmsModuleImportExportRepository.class); /** Module log. */ private CmsModuleLog m_moduleLog = new CmsModuleLog(); /** The admin CMS context. */ private CmsObject m_adminCms; /** Cache for module hashes, used to detect changes in modules. */ private Map m_moduleHashCache = new ConcurrentHashMap(); /** Timed cache for newly calculated module hashes, used to avoid very frequent recalculation. */ private Map m_newModuleHashCache = CacheBuilder.newBuilder().expireAfterWrite( 3, TimeUnit.SECONDS). build().asMap(); /** * Creates a new instance.

*/ public CmsModuleImportExportRepository() { } /** * Deletes the module corresponding to the given virtual module file name.

* * @param fileName the file name * @return true if the module could be deleted * * @throws CmsException if something goes wrong */ public synchronized boolean deleteModule(String fileName) throws CmsException { String moduleName = null; boolean ok = true; try { CmsModule module = getModuleForFileName(fileName); if (module == null) { LOG.error("Deletion request for invalid module file name: " + fileName); ok = false; return false; } I_CmsReport report = createReport(); moduleName = module.getName(); OpenCms.getModuleManager().deleteModule(m_adminCms, module.getName(), false, report); ok = !(report.hasWarning() || report.hasError()); return true; } catch (Exception e) { ok = false; if (e instanceof CmsException) { throw (CmsException)e; } if (e instanceof RuntimeException) { throw (RuntimeException)e; } return true; } finally { m_moduleLog.log(moduleName, Action.deleteModule, ok); } } /** * Exports a module and returns the export zip file content in a byte array.

* * @param virtualModuleFileName the virtual file name for the module * @param project the project from which the module should be exported * * @return the module export data * * @throws CmsException if something goes wrong */ @SuppressWarnings("resource") public synchronized byte[] getExportedModuleData(String virtualModuleFileName, CmsProject project) throws CmsException { CmsModule module = getModuleForFileName(virtualModuleFileName); if (module == null) { LOG.warn("Invalid module export path requested: " + virtualModuleFileName); return null; } try { String moduleName = module.getName(); ensureFoldersExist(); String moduleFilePath = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf( CmsStringUtil.joinPaths(EXPORT_FOLDER_PATH, moduleName + ".zip")); File moduleFile = new File(moduleFilePath); boolean needToRunExport = needToExportModule(module, moduleFile, project); if (needToRunExport) { LOG.info("Module export is needed for " + module.getName()); moduleFile.delete(); CmsModuleImportExportHandler handler = new CmsModuleImportExportHandler(); List moduleResources = CmsModule.calculateModuleResourceNames(m_adminCms, module); handler.setAdditionalResources(moduleResources.toArray(new String[] {})); // the import/export handler adds the zip extension if it is not there, so we append it here String tempFileName = RandomStringUtils.randomAlphanumeric(8) + ".zip"; String tempFilePath = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf( CmsStringUtil.joinPaths(EXPORT_FOLDER_PATH, tempFileName)); handler.setFileName(tempFilePath); handler.setModuleName(moduleName); CmsException exportException = null; I_CmsReport report = createReport(); try { CmsObject exportCms = OpenCms.initCmsObject(m_adminCms); exportCms.getRequestContext().setCurrentProject(project); handler.exportData(exportCms, report); } catch (CmsException e) { exportException = e; } boolean failed = ((exportException != null) || report.hasWarning() || report.hasError()); m_moduleLog.log(moduleName, Action.exportModule, !failed); if (exportException != null) { new File(tempFilePath).delete(); throw exportException; } new File(tempFilePath).renameTo(new File(moduleFilePath)); LOG.info("Created module export " + moduleFilePath); } byte[] result = CmsFileUtil.readFully(new FileInputStream(moduleFilePath)); return result; } catch (IOException e) { LOG.error(e.getLocalizedMessage(), e); return null; } } /** * Gets the list of modules as file names.

* * @return the list of modules as file names */ public List getModuleFileNames() { List result = Lists.newArrayList(); for (CmsModule module : OpenCms.getModuleManager().getAllInstalledModules()) { result.add(getFileNameForModule(module)); } return result; } /** * Gets the object used to access the module log.

* * @return the module log */ public CmsModuleLog getModuleLog() { return m_moduleLog; } /** * Imports module data.

* * @param name the module file name * @param content the module ZIP file data * @throws CmsException if something goes wrong */ public synchronized void importModule(String name, byte[] content) throws CmsException { String moduleName = null; boolean ok = true; try { if (content.length == 0) { // Happens when using CmsResourceWrapperModules with JLAN and createResource is called LOG.debug("Zero-length module import content, ignoring it..."); } else { ensureFoldersExist(); String targetFilePath = createImportZipPath(name); try { FileOutputStream out = new FileOutputStream(new File(targetFilePath)); out.write(content); out.close(); } catch (IOException e) { throw new CmsImportExportException( Messages.get().container(Messages.ERR_FILE_IO_1, targetFilePath)); } CmsModuleImportExportHandler importHandler = new CmsModuleImportExportHandler(); CmsModule module = CmsModuleImportExportHandler.readModuleFromImport(targetFilePath); moduleName = module.getName(); I_CmsReport report = createReport(); if (OpenCms.getModuleManager().hasModule(moduleName)) { OpenCms.getModuleManager().deleteModule(m_adminCms, moduleName, true /*replace module*/, report); } CmsImportParameters params = new CmsImportParameters(targetFilePath, "/", false); importHandler.setImportParameters(params); importHandler.importData(m_adminCms, report); new File(targetFilePath).delete(); if (report.hasError() || report.hasWarning()) { ok = false; } } } catch (CmsException e) { ok = false; throw e; } catch (RuntimeException e) { ok = false; throw e; } finally { m_moduleLog.log(moduleName, Action.importModule, ok); } } /** * Initializes the CMS context.

* * @param adminCms the admin CMS context */ public void initialize(CmsObject adminCms) { m_adminCms = adminCms; } /** * Computes a module hash, which should change when a module changes and stay the same when the module doesn't change.

* * We only use the modification time of the module resources and their descendants and the modification time of the metadata * for computing it. * * @param module the module for which to compute the module signature * @param project the project in which to compute the module hash * @return the module signature * @throws CmsException if something goes wrong */ private String computeModuleHash(CmsModule module, CmsProject project) throws CmsException { LOG.info("Getting module hash for " + module.getName()); // This method may be called very frequently during a short time, but it is unlikely // that a module changes multiple times in a few seconds, so we use a timed cache here String cachedValue = m_newModuleHashCache.get(module); if (cachedValue != null) { LOG.info("Using cached value for module hash of " + module.getName()); return cachedValue; } CmsObject cms = OpenCms.initCmsObject(m_adminCms); if (!CmsStringUtil.isEmptyOrWhitespaceOnly(module.getImportSite())) { cms.getRequestContext().setSiteRoot(module.getImportSite()); } cms.getRequestContext().setCurrentProject(project); // We compute a hash code from the paths of all resources belonging to the module and their respective modification dates. List entries = Lists.newArrayList(); for (String path : module.getResources()) { try { List resources = cms.readResources(path, CmsResourceFilter.IGNORE_EXPIRATION, true); for (CmsResource res : resources) { entries.add(res.getRootPath() + ":" + res.getDateLastModified()); } } catch (CmsVfsResourceNotFoundException e) { entries.add(path + ":null"); } } Collections.sort(entries); String inputString = CmsStringUtil.listAsString(entries, "\n") + "\nMETA:" + module.getObjectCreateTime(); LOG.debug("Computing module hash from base string:\n" + inputString); return "" + inputString.hashCode(); } /** * Creates a randomized path for the temporary file used to store the import data.

* * @param name the module name * * @return the generated path */ private String createImportZipPath(String name) { String path = ""; do { String prefix = RandomStringUtils.randomAlphanumeric(6) + "-"; path = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf( CmsStringUtil.joinPaths(IMPORT_FOLDER_PATH, prefix + name)); } while (new File(path).exists()); return path; } /** * Creates a new report for an export/import.

* * @return the new report */ private I_CmsReport createReport() { return new CmsLogReport(Locale.ENGLISH, CmsModuleImportExportRepository.class); } /** * Makes sure that the folders used to store the import/export data exist.

*/ private void ensureFoldersExist() { for (String path : Arrays.asList(IMPORT_FOLDER_PATH, EXPORT_FOLDER_PATH)) { String folderPath = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebInf(path); File folder = new File(folderPath); if (!folder.exists()) { folder.mkdirs(); } } } /** * Gets the virtual file name to use for the given module.

* * @param module the module for which to get the file name * * @return the file name */ private String getFileNameForModule(CmsModule module) { return module.getName() + SUFFIX; } /** * Gets the module which corresponds to the given virtual file name.

* * @param fileName the file name * * @return the module which corresponds to the given file name */ private CmsModule getModuleForFileName(String fileName) { String moduleName = fileName; if (fileName.endsWith(SUFFIX)) { moduleName = fileName.substring(0, fileName.length() - SUFFIX.length()); } CmsModule result = OpenCms.getModuleManager().getModule(moduleName); return result; } /** * Checks if a given module needs to be re-exported.

* * @param module the module to check * @param moduleFile the file representing the exported module (doesn't necessarily exist) * @param project the project in which to check * * @return true if the module needs to be exported */ private boolean needToExportModule(CmsModule module, File moduleFile, CmsProject project) { if (!moduleFile.exists()) { LOG.info("Module export file doesn't exist, export is needed."); try { String moduleSignature = computeModuleHash(module, project); if (moduleSignature != null) { m_moduleHashCache.put(module, moduleSignature); } } catch (CmsException e) { LOG.error(e.getLocalizedMessage(), e); } return true; } else { if (moduleFile.lastModified() < module.getObjectCreateTime()) { return true; } String oldModuleSignature = m_moduleHashCache.get(module); String newModuleSignature = null; try { newModuleSignature = computeModuleHash(module, project); } catch (CmsException e) { LOG.error(e.getLocalizedMessage(), e); } LOG.info( "Comparing module hashes for " + module.getName() + " to check if export is needed: old = " + oldModuleSignature + ", new=" + newModuleSignature); if ((newModuleSignature == null) || !Objects.equal(oldModuleSignature, newModuleSignature)) { if (newModuleSignature != null) { m_moduleHashCache.put(module, newModuleSignature); } // if an error occurs or the module signatures don't match return true; } else { return false; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy