Dashboard > Community Wiki > ... > Modules > Handling module versions
Handling module versions Log In View a printable version of the current page.

Added by Sean McMains , last edited by Boris Kraft on Jun 23, 2008  (view change)
Labels: 

Note: This is based on my reading of some sample code and the scant notes in the Javadocs. Please jump right in and correct any errors that you see!

Deltas are a really useful way to encapsulate database changes from one version of a module to another and to have them applied automatically when that module is (re)deployed.

They are defined by subclassing SimpleContentVersionHandler in your module, creating a Delta object (or many of them), and then using your class' inherited register() method to tell Magnolia about those changes. If there's some task you'd like applied when the module is installed for the first time, you can also override the getExtraInstallTasks() method to provide another List of Task objects that will be executed regardless of the versions in question.

For example, let's take a look at some of the sample code in the Samples module. This is from info.magnolia.module.samples.setup.SamplesVersionHandler v3.5.4, and demonstrates several useful features of this framework.

First, we subclass SimpleContentVersionHandler and create a List that we can use to keep track of the tasks that will be used both for the initial installation of the module and for updates to it:

public class SamplesVersionHandler extends SimpleContentVersionHandler {
    private final List installOrUpdateTasks = new ArrayList();

Next, the author of the Samples module decided that it would be nice to replace the standard Templates, Paragraphs, and Dialogs menu items in the admin interface with ones that point directly to the appropriate definitions for the samples module instead. Accordingly, he creates tasks in the constructor for SamplesVersionHandler to add a new menu item and to remove the old one: (The tasks for the Templates menu are the only ones I'm including here, as the others look pretty much the same.)

// - replace Templates menu item
final AddSubMenuItemTask addTemplateMenuItem = new AddSubMenuItemTask("config", "sample-templates", 
    "config.menu.config.templates.samples", "info.magnolia.module.samples.messages", 
    "MgnlAdminCentral.showTree('config','/modules/samples/templates')", "/.resources/icons/16/dot.gif", "subscribers");
final String templatesMenuItemPath = "/modules/adminInterface/config/menu/config/templates";
final NodeExistsDelegateTask removeOldTemplatesMenuItem = new NodeExistsDelegateTask("Menu", 
    "Remove the Templates menu item if existent", ContentRepository.CONFIG, templatesMenuItemPath,
    new RemoveNodeTask("Menu", "Removes the Template menu item as it will be replaced with a new configuration", 
        ContentRepository.CONFIG, templatesMenuItemPath));
installOrUpdateTasks.add(removeOldTemplatesMenuItem);
installOrUpdateTasks.add(addTemplateMenuItem);

Here we see our first instance of a Task. Magnolia provides a wide variety of Task classes that can be used to accomplish a variety of things. In this case, AddSubMenuItemTask does exactly what you'd expect from the name. The NodeExistsDelegateTask acts as a conditional, and takes another Task as one of its parameters. It essentially says "if the specified node exists, then execute this other Task". In this case, the Task provided is a RemoveNodeTask, which does exactly what it sounds like.

Once these Tasks are built up, they are then added to the installOrUpdateTasks List that we had already defined. (We'll see where those actually get handed off to Magnolia in a bit.)

Next up, there are some XML files that need to be bootstrapped into the database, but only if we're updating from a previous version of the Samples module to v3.5. We'll build up a Delta object with the list of Tasks we've already assembled, plus these few new ones:

final Delta for35 = DeltaBuilder.update("3.5", "")
    .addTasks(installOrUpdateTasks)
    .addTask(new BootstrapSingleResource("Bootstrap", "Installs the CSS configuration", 
        "/mgnl-bootstrap/samples/config.modules.samples.config.cssFiles.xml"))
    .addTask(new BootstrapSingleResource("Bootstrap", "Installs the JS configuration", 
        "/mgnl-bootstrap/samples/config.modules.samples.config.jsFiles.xml"))
    .addTask(new BootstrapSingleResource("Bootstrap", "Installs the paragraph configuration", 
        "/mgnl-bootstrap/samples/config.modules.samples.config.paragraphCollections.xml"));

One cool thing to note about this is that the update(), addTask() and addTasks() methods all return a reference to the DeltaBuilder object on which they are called, which makes it easy to chain them together in this way.

Also, whenever you see a version as a String, it's generally converted internally into a Version object, which has a field for major version number, minor version number, and patch version number. If the minor version or patch version is not specified, it defaults to 0. Thus, the "3.5" we see here is treated as equivalent to "3.5.0".

When we hit v3.5.4 of Magnolia, support for the Kupu editor was officially discontinued, so the corresponding dialog definition should also be removed if we're upgrading to that version:

final Delta for354 = DeltaBuilder.update("3.5.4", "")
    .addTask(new RemoveNodeTask("Remove Kupu editor", "Since 3.5 the Kupu editor is not supported anymore", 
        ContentRepository.CONFIG, "/modules/samples/dialogs/samples-devshow-allcontrols/tabRichEdit"));

Now that we have our Deltas built up for both 3.5 and 3.5.4, we can register them:

register(for35);
register(for354);

Lastly, remember that installOrUpdateTasks List that we built up? So far, those tasks only run when we update to 3.5. The author also wants to be sure that they will run when we install the module for the first time too:

protected List getExtraInstallTasks(InstallContext installContext) {
    final List installTasks = new ArrayList(installOrUpdateTasks);
    // add the default uri task
    installTasks.add(new IsInstallSamplesTask("Default URI", "Sets a new default URI if samples are to be installed.",
        getSetDefaultPublicURITask(installContext)));
    return installTasks;
}

And that's pretty much it!


Another interesting case came up on the user list recently. Miranda Jones asked how to get a custom filter in the correct order, since Magnolia's own filters always bootstrapped first. Philippe provided this sample code for a Task that would move the custom filter into the correct spot after bootstrapping was complete:

new OrderNodeBeforeTask(
    "Move Hibernate Filter",
    "Moves the ourHibernateFilter before the cms filter",
    ContentRepository.CONFIG,
    "/server/filters/cms",
    "ourHibernateFilter")
Powered by a free Atlassian Confluence Open Source Project License granted to Magnolia International. Evaluate Confluence today.
Powered by Atlassian Confluence 2.7, the Enterprise Wiki. Bug/feature request - Atlassian news - Contact administrators