Maintaining your module
Magnolia’s module mechanism facilitates smooth version changes. The idea is that an upgrade or update should be as easy as replacing a module JAR. Magnolia uses the version handler to determine which tasks should be executed. On each startup of Magnolia, the version handler provides a list of deltas.
The info.magnolia.module.ModuleVersionHandler interface is a special class which contains a series of tasks (deltas) which are executed during installation or during specific updates.
If your module needs to handle its own installation and updates, you should provide an implementation of this interface. |
Execute delta tasks
To maintain your module, you need to excecute a versionHandler
that runs a task list of Deltas.
- What is a Delta?
-
A Delta is essentially a list of tasks that are executed during a version change. This Delta stores and applies a set of tasks that meet the specified conditions. It will only be run it all conditions are met.
Sample module version handler
package info.magnolia.module.samples.setup;
import static info.magnolia.repository.RepositoryConstants.WEBSITE;
import info.magnolia.module.DefaultModuleVersionHandler;
import info.magnolia.module.InstallContext;
import info.magnolia.module.delta.ArrayDelegateTask;
import info.magnolia.module.delta.BootstrapSingleModuleResource;
import info.magnolia.module.delta.BootstrapSingleResource;
import info.magnolia.module.delta.DeltaBuilder;
import info.magnolia.module.delta.IsInstallSamplesTask;
import info.magnolia.module.delta.NodeExistsDelegateTask;
import info.magnolia.module.delta.RemoveNodeTask;
import info.magnolia.module.delta.Task;
import java.util.ArrayList;
import java.util.List;
import javax.jcr.ImportUUIDBehavior;
/**
* Used to update previous version of samples module to the new one, as the module has major changes
* there are some tasks to perform.
*/
public class SamplesVersionHandler extends DefaultModuleVersionHandler {
public SamplesVersionHandler() {
register(DeltaBuilder.update("5.4.1", "")
.addTask(new ArrayDelegateTask("Remove samples configuration from JCR",
new NodeExistsDelegateTask("", "/modules/samples/dialogs",
new RemoveNodeTask("", "/modules/samples/dialogs")),
new NodeExistsDelegateTask("", "/modules/samples/templates",
new RemoveNodeTask("", "/modules/samples/templates")),
new NodeExistsDelegateTask("", "/modules/samples/virtualURIMapping",
new RemoveNodeTask("", "/modules/samples/virtualURIMapping"))))
.addTasks(getCommonTasks()));
register(DeltaBuilder.update("6.0", "")
.addTask(new RemoveNodeTask("Remove JSP sample site", "Magnolia Templating JSP is deprecated as of 6.0.",
WEBSITE, "/jsp-sample-site")));
}
protected List<Task> getCommonTasks() {
final List<Task> commonTasks = new ArrayList<>();
commonTasks.add(new IsInstallSamplesTask("Re-Bootstrap website content for sample pages", "Re-bootstrap website content to account for all changes",
new ArrayDelegateTask("",
new BootstrapSingleResource("", "", "/mgnl-bootstrap-samples/samples/website.ftl-sample-site.xml"),
new BootstrapSingleResource("", "", "/mgnl-bootstrap-samples/samples/dam.samples.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING))));
// We re-bootstrap twice because a simple (and single) re-bootstrap (using ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING) would NOT
// "move" an existing site definition (which might actually exist from a previous version) in the site module
commonTasks.add(new BootstrapSingleModuleResource("config.modules.samples.config.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING));
commonTasks.add(new BootstrapSingleModuleResource("config.modules.samples.config.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW));
return commonTasks;
}
/**
* Installation process will bootstrap everything in the bootstrap folder, then we have to add some extra tasks.
*/
@Override
protected List<Task> getExtraInstallTasks(InstallContext installContext) {
final List<Task> extraTasks = new ArrayList<>();
extraTasks.addAll(getCommonTasks());
return extraTasks;
}
}
Version Handler classes
To create your own version handler, you don’t have to start from scratch. There are some abstract classes for common cases which can be used.
It’s best to extend one these base classes and implement only the methods you need instead of going at it from scratch. There are two abstract version handler classes in the package |
You can extend this class 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.
If you do not specify a version handler in the module descriptor, DefaultModuleVersionHandler is used and performs some basic install tasks.
|
This class extends AbstractModuleVersionHandler
and triggers the most common installation tasks such as:
Task | Description |
---|---|
|
Bootstraps empty repositories defined in the module descriptor, grants them to the |
|
Bootstraps the necessary module repository content which is provided as multiple XML-export files under |
|
Bootstraps the module’s sample repository content which is provided as multiple XML-export files under |
|
Copies all files under |
|
Registers the necessary servlets for the module. |
Tasks
A Task
is a lightweight class with the minimal necessary code to augment configuration during module installation. The important method in the Task interface is:
void execute(InstallContext installContext) throws TaskExecutionException;
-
A
Task
should execute responsibly and respond to issues appropriately. -
To allow developers/users to fix issues at a later time, fixable or irrelevant issues should be logged and standard
InstallContext
methods used. -
A
Task
should be in place to perform backups of nodes when extensive modifications are performed, meaning that a user can refer to a pre-alteration copy. -
In the event of an unrecoverable issue, a
Task
should automatically perform aTaskExecutionException
to interrupt and cancel the module installation, update and startup. If aTaskExecutionException
is thrown, the exception message should be shown to the end user. -
Exception messages should be simple and intuitive.
Abstract tasks
A set of predefined and abstract Tasks is available in the info.magnolia.module.delta package which can be used. Here are some of the most useful:
Task | Description | ||
---|---|---|---|
|
Abstract implementation of the |
||
|
An abstract implementation of
|
||
|
An abstract implementation of a
|
||
|
Executes the abstract method on every child node. |
Delegate tasks
Here is a list of useful delegate tasks.
Task | Description |
---|---|
|
A task that simply delegates to an array of other tasks. |
|
A task that delegates to another if a condition is |
|
A task that delegates to another depending on whether a specified property exists or not. |
|
A task which delegates to another if a property has a given value. |
It is customary for modules to expose some tasks that can be re-used by other modules when needed, such as info.magnolia.module.data.setup.RegisterNodeTypeTask. The API is designed so that it should be easy for you to write your own specific Task implementations. |
Conditions
Conditions are checked prior to the installation or update of a module. They check for system configuration which can’t be automatically updated, like configuration, dependencies, and so forth. Modules register their conditions like their tasks, for each successive version.
Only if all conditions in the delta evaluate positively will the tasks of the delta be executed. The most important method in the Condition interface is:
boolean check(InstallContext installContext);
Node builder API
The info.magnolia.jcr.nodebuilder.NodeBuilder is commonly used in update tasks.
Here’s a snippet from info.magnolia.module.templatingkit.setup.STKModuleVersionHandle that demonstrates its use:
register(DeltaBuilder.update("2.0.1", "")
.addTask(new NodeBuilderTask("Add editable property to stkSection page", "Adds and sets editable property to true for sectionHeader area on stkSection pages.", ErrorHandling.logging, RepositoryConstants.CONFIG, "/modules/standard-templating-kit/templates/pages/stkSection/areas", (1)
getNode("sectionHeader").then(
addProperty("editable", Boolean.TRUE)
)
))
.addTask(new NodeBuilderTask("Add editable property to areas", "Adds and sets editable property to false on sectionHeader, footer and metaNavigation.", ErrorHandling.logging, RepositoryConstants.CONFIG, "/modules/standard-templating-kit/config/site/templates/prototype/areas", (2)
getNode("sectionHeader").then(
addProperty("editable", Boolean.FALSE)
),
getNode("footer").then(
addProperty("editable", Boolean.FALSE)
),
getNode("branding/areas/metaNavigation").then(
addProperty("editable", Boolean.FALSE)
)
))
);
1 | Here, a task is added to add the editable property to the sectionHeader . |
2 | Here, a task is added to add the editable property to the sectionHeader , footer , and the branding/areas/metaNavigation . |