Creating rules and listeners
Introduction
Since using a Java Content repository Jahia makes use of the standard JCR ObservationManager. This manager observes events on the content repository and passes them to several event listeners implementing the abstract DefaultEventListener class.
In the JCRStoreService Jahia configures several default event listeners, which are necessary to make Jahia work. These event listeners are then registered to the JCRObservationManager.
One such event listener is the JBoss Rules Listener, which allows integrators to use rules to process events. These rules can be deployed in modules (template-sets), but Jahia also adds some default rules into the core product.
Furthermore the JCRStoreService holds a configurable interceptor-chain of PropertyInterceptor implementations, which are called on getting, setting or removing of properties in nodes through Jahia's JCR node/property wrappers.
Events on content can be observed through the JCR API. System events, not directly related to content operation, are sent and received via spring.
Rules
Each module in Jahia can define its own rules, extend the rule domain specific language and add its own set of global objects.
How to add rules in a module?
Just define a rules.drl file in the resources/META-INF folder of the module. An example from the tasks module:
package org.jahia.modules.tasks.rules
//list any import classes here.
import org.jahia.services.content.rules.*
import org.slf4j.Logger
expander rules.dsl
//declare any global variables here
global User user
global Service service
global Logger logger
global Tasks tasks
rule "A welcome task for the new user"
when
A new node is created
- the node has the type jnt:user
then
Log "Creating welcome task for new user: " + node.getName()
Create task "Welcome to Jahia!" with description "We are glad to have you in our platform." for user node.getName()
end
rule "A notification about new group member"
when
A new node is created
- the node has the type jnt:member
The node has a parent
then
Log "Notifying members of the group '" + parent.getParent().getName() + "' about new member '" + node.getName()
Create task "New member in the group" with description "A new member was added to the group." for members of group parent.getParent().getName()
end
rule "A task has been created"
when
A new node is created
- the node has the type jnt:task
The node has a property assignee
then
Set the property state of the node with the value "active"
Assign permissions "rw-" on the node to the user property.getNode().getName()
end
This module adds some new rule consequences that are not part of the default DSL:
Create task "Welcome to Jahia!" with description "We are glad to have you in our platform." for user node.getName()
And it also defines a global object of type Tasks named tasks:
global Tasks tasks
How to extend the domain specific language for my rules ?
It is also very simple. You only have to add a rules.dsl file in the WEB-INF/resources folder of your module. An example from the tasks module:
[consequence][]Create task "{title}" with description "{description}" for user {forUser}=tasks.createTask({forUser}, "{title}", "{description}", drools);
[consequence][]Create task "{title}" with description "{description}" for members of group {group}=tasks.createTaskForGroupMembers({group}, "{title}", "{description}", drools);
The extension made in a module will be available for all other modules, so if your modules rely on dsl from another module think of adding this module as a dependency of yours.
Here is an example of using tasks in your own module (say, Jahia RSS Feeds module):
In the pom.xml we have:
<Jahia-Depends>tasks</Jahia-Depends>
And the META-INF/rules.drl file content is:
###############################################################################
package org.jahia.modules.rss.rules
#list any import classes here.
import org.slf4j.Logger
import org.jahia.services.content.rules.*
import org.jahia.modules.tasks.rules.*
expander rules.dsl
#declare any global variables here
global User user
global Service service
global Logger logger
global Tasks tasks
###############################################################################
rule "Notification about new RSS feed"
when
A new node is created
- the node has the type jnt:rss
The node has a property url
then
Log "Notifying root about new RSS feed " + property.getStringValue()
Create task "New RSS feed alert" with description "Read the newly added RSS feed, retrieved from '" + property.getStringValue() + "'" for user "root"
end
Note that the rule file must import the org.jahia.modules.tasks.rules.* package and have a definition for tasks global object, i.e.:
...
import org.jahia.modules.tasks.rules.*
...
global Tasks tasks
...
How to add your own global objects to your rule context for execution?
Define a Spring file in your module (META-INF/spring/module-name.xml). Define a bean of type ModuleGlobalObject. Example from the tasks module:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.jahia.services.content.rules.ModuleGlobalObject">
<property name="globalRulesObject">
<map>
<entry key="tasks">
<bean class="org.jahia.modules.tasks.rules.Tasks" factory-method="getInstance">
<property name="taskService" ref="org.jahia.services.tasks.TaskService"/>
</bean>
</entry>
</map>
</property>
</bean>
</beans>
As for the DSL all modules will share the same set of global objects so be sure that the key, you use, is unique across all modules.
Changes from Jahia 6.5 / 6.6
- The KnowledgeHelper class has moved from org.drools.spi to org.drools.core.spi
- The # sign for comments is deprecated, comments should now use java syntax : // or /* */
System events
In order to listen events sent via spring, you need to implement to ApplicationListener<ApplicationEvent> interface, replacing ApplicationEvent by the event class you want to listen to. See ApplicationListener
Module deployment events
TemplatePackageRedeployedEvent event is sent when a module is deployed on the server. ModuleDeployedOnSiteEvent is sent when a module is deployed to a site.
Login / logout events
Login and logout event can be activated, if your application need to listen to these events. You'll have to add fireLoginEvent and/or fireLogoutEvent properties in your jahia.properties file. An event will then be sent everytime a user logs in or out
JCR Events
You can extend the org.jahia.services.content.DefaultEventListener
class to create a listener on JCR events. The event listener needs to be declared in the module spring file.
If the class is named org.jahia.community.CustomerDefaultEventListener, here is how to declare it in the module Spring file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean class="org.jahia.community.CustomDefaultEventListener">
<property name="workspace" value="default"/>
</bean>
</beans>
See JCR documentation for details on the different event types.