Adding a hook to Liferay
In this article, we elaborate on Liferay hooks, which are a type of plugin for the Java-based Liferay portal. We discuss what they are and then we build one. We setup the project structure and add all the code. Then we deploy it to Liferay.
Liferay hooks
After playing around a bit with a portal, and maybe writing a few portlets for it, you probably start wondering how you can override some of the portal features. You might want to use a branded theme for the whole portal or integrate the portal login with your own database of users/own authentication system.
Liferay has quite a few types of so called “plugins”, such as themes and layout templates, which enable developers to achieve the aforementioned effects. The most powerful type of plugin is called a “hook”. Liferay hooks let you change the portal functionality itself.
Building a hook
We are going to build a simple hook that logs info about every request to Liferay. Before and after every request we will log something. This could be a useful hook if it is necessary to log the ips of the clients or if one wants to calculate how long every Liferay request takes.
Project Structure
The project structure of a hook looks very similar to that of a a regular webapp. Hooks, just like portlets, are packaged as wars. You can even deploy portlets and hooks in the same war file, although this is conceptually not a good idea, because hooks override the behavior of the whole portal, and portlets are supposed to be small pluggable components that don’t impact the portal itself.
Since we are using Maven, we use a typical Maven project structure.
Note: It is possible to build hooks using the Eclipse Liferay IDE plugin, but for this article, we choose to explain building a hook from scratch.
The files in our hook war will be the following:
- /src/main/java/com/integratingstuff/liferay/hooks/CustomPostEventAction
- /src/main/java/com/integratingstuff/liferay/hooks/CustomPreEventAction
- /src/main/resources/portal-hook.properties
- /src/main/webapp/WEB-INF/liferay-hook.xml
- /src/main/webapp/WEB-INF/liferay-plugin-package.properties
- /pom.xml
We see that all the typical Maven directories: src/main/java for the Java files, src/main/resources for the resource file and src/main/webapp for the web content. The file that defines our hook, liferay-hook-xml, resides in this last directory.
Note: It is not necessary to have a web.xml file in your source code. Upon hook deploy however, Liferay will automatically create it for the deployed war.
Defining hooks
Hooks are defined in a liferay-hook.xml file.
The liferay-hook.xml for our portlet looks like this:
<hook> <portal-properties>portal-hook.properties</portal-properties> <!-- <custom-jsp-dir>/WEB-INF/jsps</custom-jsp-dir> --> <struts-action> <struts-action-path>/portal/layout</struts-action-path> <struts-action-impl>com.integratingstuff.liferay.hooks.CustomLayoutAction</struts-action-impl> </struts-action> <service> <service-type>com.liferay.portal.service.UserLocalService</service-type> <service-impl> com.integratingstuff.liferay.hooks.CustomUserLocalServiceImpl</service-impl> </service> --> </hook>
It is only overriding some portal properties, but for completeness, we also comment on the most common and important other uses of hooks in this section.
Overriding portal properties
We are only supplying a portal.properties file to override some properties of our portal(this can be done on portal level also, but note that pointing to hook classes in the actual portal.properties file is not a good idea since theses classes are not on the classpath of the portal itself).
Not all portal properties can be overridden with a hook. You can read which properties can be overridden by taking a look at the liferay hook dtd wiki page.
Overriding portal jsps
There are some other tags that can be used in a liferay-hook.xml file.
It is possible to override portal specific jsps by supplying a custom-jsp-dir and then putting jsps in that directory. For example, if we define a custom-jsp-dir “/WEB-INF/jsps” and then we create /WEB-INF/jsps/html/portlet/blogs/view.jsp in our project structure, this portal specific jsp will be overridden. The one from the hook will be used instead of the portal one.
Overriding portal services
Liferay contains a lot of services that are defined as spring beans in … of the portal source code. All these services can be overridden by using the service tag of liferay-hook.xml. For example, we could replace the portal implementation of the com.liferay.portal.service.UserLocalService – the service that enables one to save/update/lookup information about portal users – interface with our own this way.
Soon: overriding actions
As stated in this blog article, in the next releases of Liferay, it will be possible to override Liferay Struts actions with hooks as well.
Differently from Liferay services, these Struts actions are not defined as Spring beans, and in the past, they required the EXT model to be overridden. These Struts Liferay actions are concrete classes, not interfaces, are part of the internal Liferay code and dependent heavily on its implementation. It often is not a good idea to override them, since changing your version of Liferay would likely break them, but still, it is not uncommon to override some of the important ones like LayoutAction(to get custom rendering behavior of any page fragment) and LoginAction(to get custom portal login functionality that can not be covered by the Liferay event model) if need be.
Soon: adding servlet filters
Apparently, soon it will also become possible to add servlet-filter and servlet-mapping declarations to your hook, making it possible to add custom servlet filters to Liferay itself.
Our hook
Our portal-hook.properties file looks like this:
servlet.service.events.pre=com.integratingstuff.liferay.hooks.CustomPreEventAction servlet.service.events.post=com.integratingstuff.liferay.hooks.CustomPostEventAction
Basically, we are adding events before Liferay starts to process a request(but after any Liferay servlet filter) and after the processing of every request.
As stated in the comments of the portal.properties documentation(http://www.liferay.com/community/wiki/-/wiki/Main/Portal+Properties+6.0.5), when overriding Portal Events properties, we have to point to classes that extend com.liferay.portal.kernel.events.Action.
So when implementing these classes, we have to extend this Action class:
public class CustomPreEventAction extends Action{ public void run(HttpServletRequest request, HttpServletResponse response) throws ActionException { log.info(“Request from ip: ” + request.getRemoteAddr(); request.setAttribute(“startOfRequest”,new Date()); } } public class CustomPostEventAction extends Action{ public void run(HttpServletRequest request, HttpServletResponse response) throws ActionException { Date now = new Date(); Date startOfRequest = request.getAttribute(“startOfRequest”); if (startOfRequest != null){ log.info(“Request ook: ” + (now.getTime() - startOfRequest.getTime())+ “ms”); } } }
Note though that the request and response that are passed in these actions are actually wrapper objects that are managed by Liferay itself.
Some things cannot be done with these wrapper objects. Invalidating or modifying the session of the request for example.
Other files
Optional: liferay-plugin-package.properties
There is also the optional liferay-plugin-package.properties file. It is good practice to add it. It also offers some extra features.
name=IntegratingStuffDemo-hook module-group-id=liferay-ee module-incremental-version=1 tags= short-description= change-log= page-url=http://www.liferay.com author=Liferay, Inc. licenses=EE #portal.dependency.jars=portal-impl.jar, struts.jar, commons-logging.jar, log4j.jar, slf4j-api.jar, slf4j-log4j12.jar, commons-codec.jar
Notice the commented line. With portal.dependency.jars you can supply jars that have to be copied from the portal root to the lib folder of the hook. This way, you can easily write a hook that depends on the portal implementation for example(for example, if you want to override one of the Struts actions that are part of portal-impl, with struts-action).
Even if you do not specify any portal.dependency.jars, Liferay will still copy its log4j.jar, commons-logging.jar and util-java.jar into the lib folder of the hook. Adding these this way(or having it in the lib folder of the war) is not necessary.
pom.xml
If you are using Maven, you will need the following dependencies in your pom.xml file, of which some are Liferay specific:
<dependency> <groupId>com.liferay.portal</groupId> <artifactId>util-java</artifactId> <version>6.0.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.liferay.portal</groupId> <artifactId>portal-service</artifactId> <version>6.0.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
Deploying the hook
Deploying the hook is very straightforward. You can just drop the resulting war in the deploy directory of your Liferay install or add it to your server in your Eclipse/other IDE.
Best introduction to hooks. Thank you
Was this answer helpful?
LikeDislikeVery nice info, thanks!
Was this answer helpful?
LikeDislikeNow i can create hooks pretty fast
Was this answer helpful?
LikeDislikeVery nice article to start with hook. I have created an application when i was working with liferay in my company cas authenticates user(based on my company login) and if user is authenticated and not available in liferay db user will be created at runtime and show with home screen. I will soon post an blog with complete step involved in that.
The reason why i have pointed this was initially when i thought of developing an application like this i thought making use of a hook later i under stook hook will work once user is logged in(if iam not wrong).
Thanks once again Steffen for sharing very good information on integration
Was this answer helpful?
LikeDislikeThis is what iam looking from long time. can you share some more information
Was this answer helpful?
LikeDislike