Community Wiki

Print Properties
Portal Hook Plugins

Lately, Liferay has been following a trend toward externalizing extensibility features as much as possible. The traditional EXT model, while powerful, often proves too complex for many situations, or too intrusive to the product to retrain a high level of maintainability and a comfortable upgrade path because by it's very nature it inadvertently promotes bad programming practices which can easily introduce difficult migration issues.

There are still use cases for using the EXT extension model (some less component friendly app servers for example), but the plan is to minimize the need and outright eliminate it wherever possible.

That in mind, the external extensibility features of Liferay have been very un-trendily dubbed "plugins". Liferay supports 5 different types of plugins out-of-the-box. They are:

  • Portlets
  • Themes
  • Layout Templates
  • Webs
  • Hooks

Today I'd like to focus on the newest addition, "Hooks". Hooks have been Brian Chan's own personal pet project for the last several months. As the name implies they allow "hooking" into Liferay. Specifically they allow you to hook into the eventing system, model listeners, jsps and portal properties. We'll begin by creating a bare hook config file where we will define some hooks as we review the different types

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
</hook>

Event Handlers

Liferay has a few event handler connection points throughout its lifecycle, designed to allow SI's to conveniently hook-in custom logic. The available events are:

  • Application Startup Events (application.startup.events)
  • Login Events (login.events.pre, login.events.post)
  • Service Events (servlet.service.events.pre, servlet.service.events.post)

Generally speaking your event implementations should extend com.liferay.portal.kernel.events.Action.

For example, presuming we have a custom event handler which should fire when the portal is starting to process any request called me.auge.ray.ServicePreAction, this class placed in the plugin work dir of <sdk_root>/hooks/rays-hook/docroot/WEB-INF/src/me/auge/ray/ServicePreAction.java, I would place the following element in the config file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
	<event>
<event-class>me.auge.ray.ServicePreAction</event-class>
<event-type>servlet.service.events.pre</event-type>
</event>
</hook>

A couple points to make about events are that an application.startup.events can only extend com.liferay.portal.kernel.events.SimpleAction as that one expects an array of companyIds for which it should be invoked. The second is that you can define as many implementations as you like for each event type. Simply repeat the event element for each one.

Language Bundles

You can install new translations or override few words in existing translations. Point to where your properties files is located, the filename must follow this pattern: Language_$[localeId].properties, where $localeId is the id of the Locale (e.g. en_US, pt_BR, etc). You can add as many <language-properties> as you want:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
	<language-properties>com/liferay/samplelocalized/resources/Language_en_US.properties</language-properties>
</hook>

Model Listeners

Model listeners are similar in behavior to portal event handlers except that these handle events with respect to models built using ServiceBuilder. These listeners implement the com.liferay.portal.model.ModelListener interface.

If you wanted to listen for new Blog posts, you might have a class called me.auge.ray.NewBlogEntryListener which looked like this:

package me.auge.ray;
import com.liferay.portal.ModelListenerException;
import com.liferay.portal.model.BaseModel;
import com.liferay.portal.model.ModelListener;
import com.liferay.portlet.blogs.model.BlogsEntry;
public class NewBlogEntryListener implements ModelListener {
	public void onAfterCreate(BaseModel arg0) throws ModelListenerException {
BlogsEntry entry = (BlogsEntry)arg0;
System.out.println("Woohoo! We got an new one called: " + entry.getTitle());
}
public void onAfterRemove(BaseModel arg0) throws ModelListenerException { } public void onAfterUpdate(BaseModel arg0) throws ModelListenerException { } public void onBeforeCreate(BaseModel arg0) throws ModelListenerException { } public void onBeforeRemove(BaseModel arg0) throws ModelListenerException { } public void onBeforeUpdate(BaseModel arg0) throws ModelListenerException { } }

You'd configure it like so:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
	<event>
		<event-class>me.auge.ray.ServicePreAction</event-class>
		<event-type>servlet.service.events.pre</event-type>
	</event>
	<model-listener>
<model-listener-class>me.auge.ray.NewBlogEntryListener</model-listener-class>
<model-name>com.liferay.portlet.blogs.model.BlogsEntry</model-name>
</model-listener>
</hook>

As with events, add as many as you like even per model.

JSPs

One of the biggest aspects of implementing the portal is of course customization of the user experience. This largely involves modifying portal jsps. The problem is of course migration from version to version where you may end up with code squew, code management, version management, and many other issues. JSP hooks are designed to aleviate some of those issues by providing a way for SI's to easily modify jsps without having to alter the core. Simply specify a folder in the hook plugin from which to obtain jsp files and the portal will automatically use those in place of existing ones in the portal. This works for any jsps in the portal, portlets, servlets, and tags. All you need to do is make sure that you follow the same folder structure off your specified folder.

For example if you specify the folder /WEB-INF/src, the changing the view for the blogs portlet would require a file in /WEB-INF/src/html/portlet/blogs/view.jsp. Configuration would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
	<event>
		<event-class>me.auge.ray.ServicePreAction</event-class>
		<event-type>servlet.service.events.pre</event-type>
	</event>
	<model-listener>
		<model-listener-class>me.auge.ray.NewBlogEntryListener</model-listener-class>
		<model-name>com.liferay.portlet.blogs.model.BlogsEntry</model-name>
	</model-listener>
	<custom-jsp-dir>/WEB-INF/jsps</custom-jsp-dir>
</hook>

Portal Properties

We can alter the portal's configuration properties by specifying an override file. The properties in this file will immediately take effect when deployed thus allowing runtime re-configuration of the portal.

If you had a file /WEB-INF/src/portal.properties, the configuration would look like:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 5.1.0//EN" "http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd">

<hook>
	<event>
		<event-class>me.auge.ray.ServicePreAction</event-class>
		<event-type>servlet.service.events.pre</event-type>
	</event>
	<model-listener>
		<model-listener-class>me.auge.ray.NewBlogEntryListener</model-listener-class>
		<model-name>com.liferay.portlet.blogs.model.BlogsEntry</model-name>
	</model-listener>
	<portal-properties>portal.properties</portal-properties>
	<custom-jsp-dir>/WEB-INF/jsps</custom-jsp-dir>
</hook>

Finally, all of the above hooks will immediately revert their targetted functionality as soon as they are undeployed from the portal. Also, each type of hook can easily be disabled via portal.properties (Note that if properties hook is disabled, a hook cannot be used to re-enable it). Hooks can be built, packaged, and deployed, like other plugins, using the Liferay plugins SDK.

 

2097 Views, 0 Attachments 0 Attachments

  • Comments
Threaded Replies Author Date
There isn't any <language-properties> element in... Van Hoai 10/11/08 8:56 AM
should be liferay-hook_5_2_0.dtd Arcko Duan 10/12/08 7:34 PM
Yes, in 5.2, there are only two elements: ... Jonas Yuan 12/29/08 5:43 PM
It is not clear to me, where I would need to place... Thomas Kellerer 10/22/08 12:23 AM
I guess your confusion is like mine, originally...... Alex Wallace 10/31/08 12:55 PM
So this only works with the ext-enviroment, right? Thomas Kellerer 11/10/08 3:38 AM
It works in both Plugins SDK and Ext. The... Jonas Yuan 12/29/08 5:38 PM
Please, what about Webs plugins? Thank you in... Ivano Carrara 11/5/08 2:33 AM

There isn't any <language-properties> element in the DTD. There should be another version of DTD???

Posted on 10/11/08 8:56 AM.

Top Top
should be liferay-hook_5_2_0.dtd

Posted on 10/12/08 7:34 PM in reply to Van Hoai.

Top Top
It is not clear to me, where I would need to place the hook config file?
Especially in an installation where the ext-environment is not used.

Posted on 10/22/08 12:23 AM.

Top Top
I guess your confusion is like mine, originally... I looked at https://lportal.svn.sourceforge.net/svnroot/lportal/plugins/trunk/hooks/ to figure it out...

There is no create.sh in the hooks folder like other plugins.

Seems to me that:

1- you create a folder in hooks, with an arbitrary name
2- your hooks config goes in yourFolder/docroot/WEB-INF/liferay-hook.xml
3- your overrides go in /docroot/WEB-INF/src and need to be referenced by your hook config
4- you should have a yourFolder/docroot/WEB-INF/liferay-plugin-package.properties
5- you probably want to add a yourFolder/build.xml for ant

Posted on 10/31/08 12:55 PM in reply to Thomas Kellerer.

Top Top
Please, what about Webs plugins?

Thank you in advance!

Ivano Carrara

Posted on 11/5/08 2:33 AM.

Top Top
So this only works with the ext-enviroment, right?

Posted on 11/10/08 3:38 AM in reply to Alex Wallace.

Top Top
It works in both Plugins SDK and Ext.

The development of hooks should be merged with that of portlets. That is, we should develop portlets in plugins SDK with hooking feature. Right?

Thanks

Jonas Yuan

Posted on 12/29/08 5:38 PM in reply to Thomas Kellerer.

Top Top
Yes, in 5.2, there are only two elements:

<portal-properties>portal.properties</portal-properties>
<custom-jsp-dir>/META-INF/custom_jsps</custom-jsp-dir>

place all other values (including language properties) as properties in:

<portal-properties>portal.properties</portal-properties>

Posted on 12/29/08 5:43 PM in reply to Arcko Duan.

Top Top