Understanding Jahia modules

November 14, 2023

Introduction

Modules are packages that can be deployed on a Jahia platform to extend or modify it, they act like plug-ins.

Modules must respect some structure rules and strict namings conventions.

Modules cover a wide area of use cases, for example

  • Templates and pre-packaged sites
  • Front end features like components, content structures, object renderings (views) ...
  • Back-end features, frameworks.

As a Jahia developper you will work mainly with modules to deliver the features expected by your users.

A module can contain

  • CND files (definitions)
  • Views (rendering scripts)
  • Resource Bundles
  • Java classes
  • Content (xml or zip) and assets (files: images, css, js)
  • Spring files
  • Rules
  • Workflow files
  • Permissions and roles

Make a module available on a jahia platform

Modules have to be first deployed on the server to be available. See deployment for more information about deployment on the server.

Modules can then be deployed to web projects with the studio, by using the "deploy" menu, or with the "Templates and JahiApps management" panel in the Jahia Administration.

When deploying a module to a site, the content of the module is copied to the site node (until version 6.5). Templates and components folder are synchronized : the content of the template nodes and the components which have been removed in a module will be deleted from the site templates and components nodes. Every template node has a property j:sourceTemplate which holds a reference to the original template in /templateSets : this is used to identify from which module a deployed template comes from, and avoids overriding or removing templates which come from other modules. Other nodes (default pages, content, files, ...) are simply copied to the site.

If a new version of the module is uploaded on the server, it will be automatically deployed on all sites that is currently using it. All updates will be immediately available in the site.

 

Modules types

Modules are organized in different categories, depending on their roles. These categories are for information purpose only - they are to help the user to understand what the module is providing. However, they do not really limit what can be included in the module.

  • System module - A system module adds system-wide features, like a connector to an external system or additional configuration. They can contain Spring configuration files.
  • Simple module - Simple module mainly adds components and associated views.
  • Templates set - They are used as a basic structure for a site. All web projects require a template set, and only one templates set can be deployed to a site. They should include only JSPs, CSSs, images, resources, and templates.
  • JahiApp - The JahiApps are full applications that can be deployed to a site to add complex features. They include components, views, templates, actions, and any other specific Spring configuration.
  • My space module - Can be deployed to the "My space" page to extend the feature proposed to the user in its own space. They can be as complex as a JahiApp.

Module directory structure

Maven2 directories

Jahia Modules use Maven 2 directory structure (see Maven Standard Directory Layout)

src 
| + main 
| + java 
| + resources 
| + webapps 
| + site

Jahia module directory

Jahia directory is structured as this:

 

webapps 
| + javascript - contains all javascripts for the module 
| + css - contains all css for the module 
| + resources - contains all resources for the module
| + icons - contains all icons for the module content definitions 
| + META-INF 
     | + spring - Spring XML files 
| - definitions.cnd - definitions for the module 
| - rules.drl - module rules 
| - import.xml - content imported when the module is loaded the first time

Notes

  • icons naming:
    • Use the name of the definition replacing ":" by "_".
    • Use png format example: jnt:news needs jnt_news.png icon
  • resources name:
    • From the pom.xml file, get the module name (in name/ tag) and remove spaces
    • Example: for role module, its name is Jahia Roles, its resource files will be named JahiaRoles.properties

Components

A component is a content archetype usable by the editor to create content. They appear in the left panel selector, and can be dragged to an area inside the page.

Declaring components

Components can be declared in a module through a node type definition. All components that are declared in a module are deployed to a tree in the JCR, under the /templateSet node. For every node type definition found, a node jnt:component is created. Components are organized in components folder of type jnt:componentFolder. The administrator can browse these nodes using the component manager:

Components organization

Components are organized by inheriting from special mixin types. Any module can create a new component folder by creating a mixin type inheriting from jmix:droppableContent.

For each mixin type which inherits from jmix:droppableContent, like jmix:basicContent, a component folder is displayed.
If a type inherits from a mixin derived from jmix:droppableContent, it will be stored in the corresponding component folder.
Components that do not inherit from jmix:droppableContent are stored in a folder named nonDroppableComponents.

Modules deployment

Components, views and options, provided by a module, are only available to a site once the module is deployed on it. See modules page.

Studio-only components

Components can inherits from jmix:studioOnly. These components are intended to be used for template development only. They will be available only in the studio, and will not be deployed to sites.

Setting rights on components

The component manager can be used to restrict access to some users or groups on different components. If a user does not have access to a component, it will disappear from the selector and all create engines. However, she/he will still be able to edit existing nodes based on these components.

Use the "Component permissions" tab to change the permissions:

By default, rights are inherited from the site. Anybody who has access to the edit mode can use all components. In order to restrict permissions, remove permissions from the site-privileged group, then add the users and / or groups who should be able to access the component.
 

Modules deployment 6.5 / 6.6

6.5/6.6

Deployment principles

Modules are deployed by copying the war file into /WEB-INF/var/shared-modules. When a file is detected here, Jahia deploys the module by executing the method deployPackage() which unzips the content to /modules.

Module dependencies are then checked. If all dependent modules are correctly deployed, the module is added to the registry :

  • Load all definitions into the JCR service
  • Compile and register the new rules
  • Load all resource bundles

A spring event (TemplatePackageRedeployedEvent) is then sent to flush all internal caches that may be affected by a module redeployment

Finally, the modules spring context is reloaded. Once the context has been reloaded, Jahia executes these final tasks (in the case of a cluster, only the processing server executes these tasks) :

  • Execute the import if any import file is packaged into the module
  • Register the components into the JCR
  • Update all sites with the new module version

The same procedure is executed when deploying a new module or updating an existing one.

Deploy modules with classes

A module without classes or libraries can be hotdeployed when the server is running. However, if a module adds a new class or updates an existing one, the server must be properly shut down when deploying the module. When restarting, the classes and libraries must be correctly deployed to WEB-INF/lib or WEB-INF/classes. Jahia provides two ways to do that :

The tomcat provided with the installer comes with a listener which deploys automatically classes and libraries at startup. So, if you use the standard tomcat, you'll just have to copy the war file to /WEB-INF/var/shared-modules .
Jahia also comes with a script deployModule.sh which can be used to deploy the modules. It will copy the file to /WEB-INF/var/shared-modules and the included libraries and classes to /WEB-INF/lib and /WEB-INF/classes .

Cluster deployment

In cluster environments, we must differentiate between cold deployment and hot deployment of modules.

Cold deployment

Cold deployment means that modules are being deployed when the cluster is completely shutdown. In this case, cold deployment should be done on all the nodes using the deployModules.sh script. When starting up the cluster, it is critical that the processing server be started first, and that it's initialization is completed before starting the other nodes.

Hot deployment

For hot deployments of modules (when the cluster is up and running), installation on different cluster nodes should be done in sequence, but the processing server must be the last one on which the module is deployed. Modules will be available only when they has been deployed on the processing server.

If the module contains new classes or libraries, or classes or libraries updates, the server must be properly shut down before the deployement, as explained in Deploy modules with classes. The module can be deployed on every server, one after the other. Deploy on the first server, restart it, then go to the second server, and so on. The processing server must be the last one on which the module is deployed. There should be no downtime of the whole cluster.

Modules deployment 7.x

7.0

DX 7.0 introduced the ability to use OSGi to package and distribute Jahia modules. This means that you can now dynamically deploy and undeploy modules into a Jahia installation, making it easier to manage modules during both development and production phases.

OSGi modules are JAR files. To deploy a module, you just need to copy it into digital-factory-data/modules. This can be done whether the server is running or not, it will be picked up and deployed by the server. Every resource in the module (provided it deployed without any errors) will be immediately accessible, including any libraries that have been embedded in the package. If you have declared services in your bundle, they will also be available upon deployment.

If you are using the Jahia Maven Plugin you can simply redeploy a module from the project using the jahia:deploy goal again. Upon deployment in the digital-factory-data/modules directory, Jahia will automatically undeploy the old version and deploy the new one.

Alternatively you can also use the administration’s manage modules UI to undeploy the module.

For more information, take a look at the OSGi module development documentation.

Cluster deployment

In cluster environments, we must differentiate between cold deployment and hot deployment of modules.

Cold deployment

Cold deployment means that modules are being deployed when the cluster is completely shutdown. Simply copy the jar file of the module on each cluster node in the digital-factory-data/modules folder. When starting up the cluster, it is critical that the processing server be started first, and that it's initialization is completed before starting the other nodes.

During cold deployement, you must stop ALL Jahia instances

Hot deployment

7.0.x / 7.1.x

For hot deployments of modules (when the cluster is up and running), installation on different cluster nodes should be done in sequence, but the processing server must be the last one on which the module is deployed. Modules will be available only when they have been deployed on the processing server. The main difference with previous versions (6.5/6.6) is that, thanks to Osgi, the servers do not need to be restarted anymore, even if the module(s) contains classes or libraries.

In 7.1.x, if the parameter autoStartNewModuleVersion is set to true,  you have to deploy your module on the processing server first.

7.2.x

Thanks to Cellar, you can deploy your module on only one node to have it propagated among all the cluster. You can get here more information regarding Cellar.

Note, please, that module deployment can be done on any node (processing or browsing). The module is ready to use as soon as the import on the processing node is finished (so the processing node needs to be started to finish the import of a new module).

 

Use Maven with a proxy 

As our Maven repository is on an https server, you have to add the following arguments to your maven install command :

  -Dhttps.proxyHost=yourproxy.com
  -Dhttps.proxyPort=8080
  -Dhttps.proxyUser=user
  -Dhttps.proxyPassword=password

You can also add these parameters to your MAVEN_OPTS system variable.

Example to create a new module from archetypes:
mvn archetype:generate -Dhttps.proxyHost=yourproxy.com -Dhttps.proxyPort=8080 -Dhttps.proxyUser=user -Dhttps.proxyPassword=password -DarchetypeGroupId=org.jahia.archetypes -DarchetypeArtifactId=jahia-module-archetype

BoundComponents

BoundComponents mechanism allows to define in a module that this module need to be linked to another module to be fully effective.

For example, we can define a calendar module that will drive a list of news by displaying links on the days that have news (with the number of news per day).

This can be extended to allow our calendar to interact with any type of list of content.

  • We will see first how to define a bound component.
  • Then how to refer to the bound element to drive it.
  • How the user can define the bound between two component.

Define a bound component:

In your cnd file, your definition should inherit the mixin type: <jmix:bindedComponent>. This mixin defines a simple weakreference as a property.

[jmix:bindedComponent] mixin
- j:bindedComponent (weakreference)

A usage example :

 

[jnt:tagging] > jnt:content, mix:title, jmix:siteComponent, jmix:bindedComponent

This definition only defines that the tagging element has to be bound to be effective.

Another usage example :

[jnt:calendar] > jnt:content, jmix:editorialContent, jmix:bindedComponent
- startDateProperty (string,choicelist[linkerProps=j:bindedComponent])
- endDateProperty (string,choicelist[linkerProps=j:bindedComponent])

This definition extends the fact that the calendar component needs to be bound to another component and also adds two properties that are initialized based on the list of properties available from the bound component. If I bind my calendar to a list of news, I will have access to all the news properties to drive the list, and I should choose a date property to make my calendar work.
 

How to refer to the bound element to drive it

In your module files just access to the property j:bindedComponent to have access to the bound component and use it as you would.

An example from the calendar module, that fill a map with the number of node per date.

<c:set var="boundComponent" value="${uiComponents:getBindedComponent(currentNode, renderContext, 'j:bindedComponent')}"/>

<c:forEach items="${linked.node.nodes}" var="linkedChild" varStatus="status">
<fmt:formatDate pattern="yyyy-MM-dd" value="${linkedChild.properties[currentNode.properties.startDateProperty.string].date.time}" var="startDate"/>
<c:choose>
    <c:when test="${empty datas[startDate]}">
        <c:set target="${datas}" property="${startDate}" value="1"/>
    </c:when>
    <c:otherwise>
        <c:set target="${datas}" property="${startDate}" value="${datas[startDate]+1}"/>
    </c:otherwise>
</c:choose>
</c:forEach>

How the user can define the bound between two component ?

To allow the user to define to which component your module will be bound you have to define a draggable element in your module file. This draggable element will be drop by the user to the element he want to bound to your module.

To define this element just use the tag <<template:linker>>.

A simple example from calendar :

<template:linker path="*"/>

A more advanced example from tagging, that define that on the bound component we need to add a mixin type :

 
<template:linker path="*" mixinType="jmix:tagged"/>

Module map

This map is use to share objects within modules in an ajax context.

write :

 

<c:set target="${moduleMap}" property="editable" value="true" />

read :

${moduleMap.editable}

Default module

Overview

This module contains common content definitions, rendering templates, resource bundles and static assets and is deployed by default.

The module includes a set of features and components, providing base Jahia templates functionality:

  • Hide templates - allows removing a particular template from the drop down list of available templates
  • Navigation menu - support for building navigation menus manually and in automatic mode
  • Lists - support for managing lists in Jahia
  • Tagging - utilization of social tagging feature in Jahia
  • User Profile - general instructions on how to develop in line editable pages is available in user profile page
  • User Agent - how to create specific templates per user agent

Configuration options

Module configuration options

Depending on your needs, you might want to parameterize some aspects of you modules so that you could adapt its capabilities to your specific deployment environment. You have several options to do so as detailed below.

Spring configuration

Your module components can depend on Spring beans that can be configured via an XML descriptor found in your module's META-INF/spring/<module-name>.xml file. Here you can define beans for your components, inject other components (e.g. Jahia services) or specify properties that will be resolved when Jahia instantiates the components in your module. Please refer to the Spring documentation for more details.

OSGI Configurations

Sometimes you want to be able to deploy OSGI configurations files at module deployment, this is possible by adding your configuration files into your module source folder under /src/main/resources/META-INF/configurations

Example from our Elastic search - search provider module:

ES-config1.png

This will deploy automatically the configuration files in the /digital-factory-data/karaf/etc folder.
The OSGI framework will automatically register the configuration during runtime.

Note that a configuration file will be overriden each time the module is deployed. This is useful if the configuration file needs to evolve in futur versions of your module. If you want to avoid the override of the configuration file during module upgrade, you need to start the first line of your configuration file with the marker # default configuration

Example from our Elastic search - search provider module:

ES-config2.png

Module properties

The Jahia runtime also loads automatically Java-style properties files and make the resulting properties available to your module's Spring configuration. However, to avoid loading potentially useless properties files, Jahia only looks for files conforming to the following patterns in your module's classpath:

jahia-modules-*.properties
jahia/jahia-modules-*.properties

Since changing these properties require access to the module's source code and ability to re-build/re-deploy it, we also provide, in DX 7.0.0.2 and above, a way to override these properties using the jahia/jahia.properties file located in your Jahia externalized configuration directory (named digital-factory-config by default).

These properties can then be accessed in your Spring configuration descriptor using the ${<property name>} syntax. You can also provide an optional default value that will be used if no property with the provided name can be resolved using the ${<property name>:<default value>} syntax. For example, to set the foo property to the value of the mymodule.bar property with the default default value, you'd use the following property setter in your bean definition: <property name="foo" value="${mymodule.bar:default}" />.