Model Listener hook's are a type of Liferay plugin that listens for events taken on an model and executes code in response. Model Listener hooks are similar to Custom Struts Action hooks in that they respond to an action taken in the portal. However while Struts actions respond to an action taken by a user, a Model Listener responds (before or after) an event involving a Liferay model.
Here are a few examples of Struts Actions v. Model Listeners for comparison.
The best resource for learning Liferay's architecture is through their source code. All their source files are located on GitHub and by viewing their JavaDocs. You can see all of the core portal models on the JavaDocs and all of the Struts Actions on the GitHub.
In this tutorial we are going to develop a Model Listener that sends an email to a User after their account is first created. To do this we are going to write a class called UserModelListener which will extend Liferay's BaseModelListener. We will briefly go over hook creation and will cover the necessary modifications to the following config files
To begin developing your Model Listener hook you must first launch your Liferay IDE or Liferay Developer Studio application.
Both the Liferay IDE and the Liferay Developer Studio are customized Eclipse development environments. They are strikingly similar and one set of directions should be sufficient for both environments.
Inside your development environment execute the following steps.
You will spawn this window.
Please enter the information as shown above
Make sure that your project is located inside your Liferays Plugins SDK Hook directory. You will need to select your SDK and your Runtime accordingly.
In your Package Explorer perspective you will see the following directory structure.
Now that you have created your hook you will need to create your custom UserModelListener class. This class will extend Liferay's BaseModelListener class.
Liferay's BaseModelListener class is an abstract class that implements the ModelListener interface. You do not want to implement the ModelListener interface directly as it will require you to override all of it's methods.
The following methods are provided to you by the ModelListener interface through the BaseModelListener abstract class.
- onAfterAddAssociation
- onAfterCreate
- onAfterRemove
- onAfterRemoveAssociation
- onAfterUpdate
- onBeforeAddAssociation
- onBeforeCreate
- onBeforeRemove
- onBeforeRemoveAssociation
- onBeforeUpdate
Create your UserModelListener class inside the following directory. To create the class via the GUI simply execute the following commands
docroot/
WEB-INF/
src/
Enter the information shown below
Inside your UserModelListener class paste the following code
package com.example.hook;
import com.liferay.mail.service.MailServiceUtil;
import com.liferay.portal.ModelListenerException;
import com.liferay.portal.kernel.mail.MailMessage;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.model.BaseModelListener;
import com.liferay.portal.model.User;
import javax.mail.internet.InternetAddress;
public class UserModelListener extends BaseModelListener<User> {
private User user = null;
@Override
public void onAfterCreate(User user) throws ModelListenerException {
this.user = user;
if(isValidEmail()) {
sendEmail("[email protected]", user.getEmailAddress(), "Welcome!", "Your account is created!");
}
}
private boolean isValidEmail() {
return Validator.isNotNull(user.getEmailAddress()) && Validator.isAddress(user.getEmailAddress());
}
private void sendEmail(String from, String to, String subject, String body) {
try {
MailServiceUtil.sendEmail(new MailMessage(new InternetAddress(from), new InternetAddress(to), subject, body, false));
} catch (Exception e) {
System.err.print("E-Mail spawned by User Model Listener failed to " + user.getFullName() + " with message " + e.getMessage());
}
}
}
In order to configure the association between our custom listener and our model we need to make a few final adjustments. Firstly, create a new portal.properties file in the following directory.
docroot/
WEB-INF/
src/
+ portal.properties
Only one line needs to be added to the file.
value.object.listener.com.liferay.portal.model.User = com.example.hook.UserModelListener
We can generalize this by saying, for any Model Listener we create the associated properties value must be in the form of
value.object.listener.fully.qualified.model.name = fully.qualified.listener.name
In other words if we wrote a CustomerDLFolderModelListener class, packaged inside com.example.code, for the DLFolder model we would have the following property
value.object.listener.com.liferay.portal.model.DLFolder = com.example.code.CustomerDLFolderModelListener
Lastly, locate your liferay-hook.xml file. In Source view, write the following.
<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd">
<hook>
<portal-properties>portal.properties</portal-properties>
</hook>
To see what other elements can be used in this XML file you can reference the URL within the DocType Definition. This is standard for all XML and SGML files with a DTD. Another example of a Liferay XML file with a DTD is service.xml (Liferay's ORM implementation based on Hibernate).
Building and deploying hooks is a simple process. Liferay Plugin development supports build and dependency automation with
In our example we utilized Ant for build automation. The build.xml file contains the build commands (known as targets in Ant). To build your hook simply execute the following commands.
In your console view you should see something similar to the following
Buildfile: C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\build.xml
all:
clean:
[delete] Deleting directory C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\docroot\WEB-INF\classes
clean-portal-dependencies:
compile:
merge:
compile-import-shared:
[mkdir] Created dir: C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\docroot\WEB-INF\classes
[copy] Copying 5 files to C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\docroot\WEB-INF\lib
compile-java:
[copy] Copied 3 empty directories to 3 empty directories under C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\docroot\WEB-INF\classes
[javac] Compiling 1 source file to C:\liferay-plugins-sdk-6.2-ee-sp11\hooks\User-Listener-Hook-hook\docroot\WEB-INF\classes
merge:
war:
clean-portal-dependencies:
[zip] Building zip: C:\liferay-plugins-sdk-6.2-ee-sp11\dist\User-Listener-Hook-hook-6.2.10.1.war
deploy:
[copy] Copying 1 file to C:\liferay-portal-6.2-ee-sp11\deploy
BUILD SUCCESSFUL
Total time: 7 seconds
With your Hook successfully built it is now time to start your portal and deploy it. To start your server and deploy the Hook locate the Server perspective.
Please let me know if you have any questions, comments, concerns, etc. All constructive feedback is greatly appreciated!