How to create a custom module?

Creating a module

By using maven you can easily create a module skeleton by using our archetype. A initial module is build in the directory you called the command and it will contain:

  • maven pom.xml
  • module class
  • module descriptor
  • bootstrap resources directory

mvn archetype:create -DarchetypeGroupId=info.magnolia \\
                     -DarchetypeArtifactId=maven-archetype-magnolia-module \\
                     -DarchetypeVersion=1.0.0-SNAPSHOT \\
                     -DgroupId=com.foo.bar \\
                     -DartifactId=my-module \\
                     -DremoteRepositories=http://svn.magnolia.info/maven/snapshots

Everything in src/main/java and src/main/resources will be finally in the by maven generated module jar file. The file src/main/resources/META-INF/magnolia/mymodule.xml is the module descriptor.

All resources found in the directory mgnl-bootstrap/mymodule will be bootstrapped during the registration process.

The files in mgnl-files are extracted to the webapp's filesystem. Here you place the jsps or files enduser like to change.

The files in mgnl-resources will stay in the jar and will be streamed directly. You can access these files using the URL prefix .resources. In this example it would be /.resource/mymodule/sample.css

Module descriptor

The module has an xml descriptor found in META-INF/magnolia/modulename.xml. The descriptor defines the name, class to load, dependencies, servlets and repositories to register.

For more details see the module.dtd in the info.magnolia.cms.module package.

Note: The descriptor is read using the commons betwixt project.

<module>
     <name>workflow</name>
     <displayName>Workflow</displayName>
     <description>Workflow using OpenWFE</description>
     <class>info.magnolia.module.owfe.Engine</class>
     <version>1.0</version>
     <dependencies>
         <dependency>
             <name>adminInterface</name>
             <version>3.0</version>
         </dependency>
     </dependencies>
     <servlets>
         <servlet>
             <class>info.magnolia.module.owfe.servlets.FlowDefServlet</class>
             <mappings>
                 <mapping>/FlowDef</mapping>
             </mappings>
             <name>FlowDef</name>
             <comment>Servlet to see flow definition.</comment>
             <params>
                 <!--              <param></param> -->
             </params>
         </servlet>
     </servlets>
 </module>

Configuration

Each module has a node in the config repository under the modules node. Each module has a subnode called config under which the module specific configuration is done.

A good example is a mailing list module needing properties like servername, port, ...

Registration

The ModuleRegistration calls the Modules method register(). The current ModuleDefinition read from the xml descriptor is passed to this method as well the node created for this module. In addition a register state constant is passed to define the state of this registration (new installation, new version, none). Where none means the module is already installed.

Warning

The method is always called (even if already registered). This gives the module a chance to register in multiple steps, to update its data ...

/**
      * No registration needed. Same version is already registered
      */
     int REGISTER_STATE_NONE = 0;

/** * First installation. Node didn't exist in the repository */ int REGISTER_STATE_INSTALLATION = 1;

/** * New version of a already registered module */ int REGISTER_STATE_NEW_VERSION = 2;

A module can request a restart of the webapp after the registration. It will set the restartNeeded flag. If this flag is set the module is not initialized and a restart page is showed to the user if he tries to access magnolia.

Before a registration is started all the dependencies are checked.

Default Implementation

The abstract class AbstractModule processes this default registration

  • create the modules node with the default properties (name, class, ...)
  • bootstrap all the files under mgnl-bootstrap/modulename/*.xml. The naming of the bootstrap files is following the shema: repository.dot.separated.path.to.node.xml
  • register servlets defined in the descriptor
  • register repositories defined in the descriptor
  • extract the files in the following folders to the filesystem: mgnl-files/*/modulename
If a bootstrap file tries to import into an already existing path, the path is deleted in advance. In the case the the parent path is not existing it is created using the node type CONTENT.

If the module needs additional tasks to do, it can overwrite the onRegistration() method. If so overwrite the onUnRegister() to.

Initialization

The Modules method init() is called after the registration of all the modules. Here you will instantiate module specifig objects, add objects to the managers (like dialogs, paragraphs, ...)

Default Implementation

Default Implementation of AbstractAdminModule

Some modules needs to register dialogs, paragraphs, tress,… It is best practice to extend the AbstractAdminModule of the admin interface package class, which does the following default initialization

  • register the virtual URI mappings in the modules subnode virtualURIMapping
  • register the dialogs in the modules subnode dialogs
  • register the controls in the modules subnode controls
  • register the trees in the modules subnode trees
  • register the paragraphs in the modules subnode paragraphs
  • register the templates in the modules subnode templates
  • register the commands in the modules subnode commands

Info

The the module can overwrite the onInit() method to execute additional tasks.

Unregister

The Modules unregister method has to undo all the tasks done during the registration process. All the nodes created through the bootstrap are deleted as well the files extracted. After the call of the unregister method the jar is moved to libs/uninstalled to avoid a reinstall by a system restart.

Warning

AbstractModule has not yet a default implementation, but will in the future

Development

ModuleUtil

The most of the tasks in the default implementation are done by using the ModuleUtil class of the module package. If you want to extract files, bootstrap content, register servlets, … it's wise to check the existence of a method in the ModuleUtil.

Resource reloading

You can set the magnolia.develop property to true in the magnolia.properties file. All the resources are read newly for each request afterward.

JSP

The JSP in mgnl-files folder are not extracted again after a change. This means that the originally extracted file still lays in the templates folder for example. But you can easely trigger the extraction again by using the Deployment found in the Tools menu.

null null