/** * This file Copyright (c) 2003-2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.module; import info.magnolia.cms.core.Content; import info.magnolia.cms.core.HierarchyManager; import info.magnolia.cms.core.ItemType; import info.magnolia.cms.core.NodeData; import info.magnolia.cms.util.NodeDataUtil; import info.magnolia.module.delta.AbstractRepositoryTask; import info.magnolia.module.delta.Condition; import info.magnolia.module.delta.Delta; import info.magnolia.module.delta.DeltaBuilder; import info.magnolia.module.delta.ModuleFilesExtraction; import info.magnolia.module.delta.Task; import info.magnolia.module.delta.TaskExecutionException; import info.magnolia.module.model.ModuleDefinition; import info.magnolia.module.model.Version; import info.magnolia.module.model.VersionComparator; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.jcr.RepositoryException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Extend this and register your deltas in the constructor using the register method. * Add your own install tasks by overriding the getExtraInstallTasks() method. * In most cases, modules won't need to override any other method. * * @see info.magnolia.module.DefaultModuleVersionHandler * @author gjoseph * @version $Revision: $ ($Author: $) */ public abstract class AbstractModuleVersionHandler implements ModuleVersionHandler { protected Logger log = LoggerFactory.getLogger(getClass()); private final Map<Version, Delta> allDeltas; public AbstractModuleVersionHandler() { allDeltas = new TreeMap<Version, Delta>(new VersionComparator()); } /** * Registers the delta needed to update to version v from the previous one. * Adds a Task to update the module version in the repository. */ protected void register(Delta delta) { final Version v = delta.getVersion(); if (allDeltas.containsKey(v)) { throw new IllegalStateException("Version " + v + " was already registered in this ModuleVersionHandler."); } delta.getTasks().addAll(getDefaultUpdateTasks(v)); delta.getConditions().addAll(getDefaultUpdateConditions(v)); allDeltas.put(v, delta); } @Override public Version getCurrentlyInstalled(InstallContext ctx) { try { log.debug("checking currently installed version of module [{}]", ctx.getCurrentModuleDefinition()); // check if this module was ever installed if (!ctx.hasModulesNode()) { return null; } final Content moduleNode = ctx.getOrCreateCurrentModuleNode(); final NodeData versionProp = moduleNode.getNodeData("version"); if (!versionProp.isExist()) { return null; } return Version.parseVersion(versionProp.getString()); } catch (RepositoryException e) { throw new RuntimeException(e); // TODO } } @Override public List<Delta> getDeltas(InstallContext installContext, Version from) { if (from == null) { return Collections.singletonList(getInstall(installContext)); } return getUpdateDeltas(installContext, from); } protected List<Delta> getUpdateDeltas(InstallContext installContext, Version from) { final Version toVersion = installContext.getCurrentModuleDefinition().getVersion(); final List<Delta> deltas = new LinkedList<Delta>(); for (Version v : allDeltas.keySet()) { if (v.isStrictlyAfter(from)) { final Delta delta = allDeltas.get(v); if (v.isEquivalent(toVersion) && !StringUtils.equals(v.getClassifier(), toVersion.getClassifier())) { delta.getTasks().add(new ModuleVersionUpdateTask(toVersion)); } deltas.add(delta); } } // if there was no delta for the version being installed, we still need to add the default update tasks if (toVersion.isStrictlyAfter(from) && !allDeltas.containsKey(toVersion)) { deltas.add(getDefaultUpdate(installContext)); } return deltas; } /** * @deprecated since 4.2, renamed to getDefaultUpdate(InstallContext installContext) */ @Deprecated protected Delta getUpdate(InstallContext installContext) { return getDefaultUpdate(installContext); } /** * The minimal delta to be applied for each update, even if no delta was specifically registered * for the version being installed. */ protected Delta getDefaultUpdate(InstallContext installContext) { final Version toVersion = installContext.getCurrentModuleDefinition().getVersion(); final List<Task> defaultUpdateTasks = getDefaultUpdateTasks(toVersion); final List<Condition> defaultUpdateConditions = getDefaultUpdateConditions(toVersion); return DeltaBuilder.update(toVersion, "").addTasks(defaultUpdateTasks).addConditions(defaultUpdateConditions); } protected List<Task> getDefaultUpdateTasks(Version forVersion) { final List<Task> defaultUpdates = new ArrayList<Task>(2); defaultUpdates.add(new ModuleFilesExtraction()); defaultUpdates.add(new ModuleVersionUpdateTask(forVersion)); return defaultUpdates; } protected List<Condition> getDefaultUpdateConditions(Version forVersion) { return Collections.emptyList(); } /** * * @see #getBasicInstallTasks(InstallContext) override this method if you need a different set of default install tasks. * @see #getExtraInstallTasks(InstallContext) override this method if you need extra tasks for install. */ protected Delta getInstall(InstallContext installContext) { final List<Task> installTasks = new ArrayList<Task>(); installTasks.addAll(getBasicInstallTasks(installContext)); installTasks.addAll(getExtraInstallTasks(installContext)); installTasks.add(new ModuleVersionToLatestTask()); final List<Condition> conditions = getInstallConditions(); final Version version = installContext.getCurrentModuleDefinition().getVersion(); return DeltaBuilder.install(version, "").addTasks(installTasks).addConditions(conditions); } protected abstract List<Task> getBasicInstallTasks(InstallContext installContext); /** * Override this method to add specific install tasks to your module. * Returns an empty list by default. */ protected List<Task> getExtraInstallTasks(InstallContext installContext) { return Collections.emptyList(); } protected List<Condition> getInstallConditions() { return Collections.emptyList(); } @Override public Delta getStartupDelta(InstallContext installContext) { final ModuleDefinition moduleDef = installContext.getCurrentModuleDefinition(); final List<Task> tasks = getStartupTasks(installContext); return DeltaBuilder.startup(moduleDef, tasks); } /** * Override this method to add specific startup tasks to your module. * Returns an empty list by default. */ protected List<Task> getStartupTasks(InstallContext installContext) { return Collections.emptyList(); } /** * The task which modifies the "version" property to the version being <strong>installed</strong>. * (from module descriptor) * TODO : make this mandatory and "hidden" ? */ public class ModuleVersionToLatestTask extends AbstractRepositoryTask { protected ModuleVersionToLatestTask() { super("Version number", "Sets installed module version number."); } @Override protected void doExecute(InstallContext ctx) throws RepositoryException, TaskExecutionException { // make sure we have the /modules node if (!ctx.hasModulesNode()) { final HierarchyManager hm = ctx.getConfigHierarchyManager(); hm.createContent("/", ModuleManagerImpl.MODULES_NODE, ItemType.CONTENT.getSystemName()); } final Content moduleNode = ctx.getOrCreateCurrentModuleNode(); final NodeData nodeData = NodeDataUtil.getOrCreate(moduleNode, "version"); nodeData.setValue(getVersion(ctx).toString()); } protected Version getVersion(InstallContext ctx) { return ctx.getCurrentModuleDefinition().getVersion(); } } /** * The task which modifies the "version" property to the version we're <strong>updating</strong> to. */ public class ModuleVersionUpdateTask extends ModuleVersionToLatestTask { private final Version toVersion; protected ModuleVersionUpdateTask(Version toVersion) { super(); this.toVersion = toVersion; } @Override protected Version getVersion(InstallContext ctx) { return toVersion; } } }