The power of Liferay Portal as a development platform lies in its customizability. That is, you, as a developer, are able to make changes to the Liferay source code directly, through a number of different methods, providing for a number of technology and business benefits. Unsurprisingly, this power comes with a high risk. If you do not make changes the "proper" way, you may introduce defects or other unintended consequences to your portal application. Therefore in many cases, Liferay has defined and provided the "proper" way, or as we here at Dunn Solutions Group call it, the "Liferay way" of creating customizations to meet clients' business requirements. The process is actually rather simple, and really just common sense.
- Work with the client to understand and massage the business requirements in order to fit them into the Liferay Portal framework. That is, put everything into a portal context first.
- Review the Liferay source and understand how Liferay is already achieving any similar or related functionality. (This step is crucial. Here at Dunn Solutions Group, our Liferay ninjas have achieved their expertise by merely just reading the source code. Attending official training courses is important to get a basic understanding. But gaining mastery is actually simple and free! Just roll up your sleeves, prepare a pot of coffee, and dig into the source code. I promise you that it will be a surprisingly rewarding experience.)
- Determine the official customization framework provided by Liferay that best allows you to construct your customization, informed by the knowledge gathered in the first two steps.
- Do the actual development, leveraging and reusing many code blocks from the second step. Using this approach, you are truly extending Liferay, and not fighting against it. Your future upgrades will be relatively painless. This is the Liferay way that we here at Dunn Solutions Group have had much success with.
We provide an example to really drive home our up-to-now theoretical ideas. A common business need is to perform some initialization upon deploying a custom portlet. How can we achieve this? Consider the following two options.
Startup Action Hook
In many Liferay training guides, tutorials, blog posts, and forum posts, developers are introduced to the idea of "hooking" into an event. That is for many Liferay events, right before and right after, you can specify custom code to execute. You can review these events by reading portal.properties, found inside portal-impl.jar. A popular example is the portal startup event. It is specified as application.startup.events in the properties file. You can then define a custom class (extending SimpleAction), and have it be associated with application.startup.events. When the portal starts up, your custom class' run method will be executed. (Since this is a common example with many online tutorials a simple search away, I will not include the details here.) It turns out that if you create a Liferay hook within your custom portlet plugin project, the run method will be executed everytime you deploy the portlet. Thus, you now have a way of running your initialization code upon portlet deployment. But is this the truly Liferay way?
Portlet Hot Deploy Listener
Recall step 2 is reviewing the Liferay source, and this includes portal.properties. Get familiar with this file. It is a treasure trove of goodies for any serious Liferay developer. And so in this case, if you did you homework (instead of just doing the knee-jerk Google search), you would have seen a property called hot.deploy.listeners. And you would have also seen a class called PortletHotDeployListener as one of the values associated with this property. Next, go and read PortletHotDeployListener and the associated classes. Go ahead. Read it and come back after you are done. Click here: PortletHotDeployListener. Finished? Great. You should have seen that this code takes information from portlet configuration files like liferay-portlet.xml, and registers them with other Liferay components. So next time you deploy a portlet, you'll now understand the log messages similar to these:
[PortletHotDeployListener:343] Registering portlets for my-portlet
[PortletHotDeployListener:490] 1 portlet for my-portlet is available for use
Solution: Custom Listener Class
Now the solution should be fairly obvious. Liferay uses the concept of hot deploy listeners to listen to and react to various hot deployment events. (You can see the other hot deploy listeners associated with that property in the properties file.) So to fulfill the business requirement of portlet initialization, just create a custom listener class and associate it with that property. Furthermore, Liferay stores portlet initialization data in XML configuration files, and parses them in the hot deploy listeners. So you should probably follow that model too by creating your own custom XML configuration files and store them in the same location.
Finally, note that the PortletHotDeployListener class extends BaseHotDeployListener (which your custom class should too), and thus, needs to override two public methods, invokeDeploy and invokeUndeploy. The first is where the initialization logic is performed. And the second cleans up the system after a portlet is undeployed. This is a great example of where if you follow the process outlined above, you will automatically adhere to best practices, and avoid unnecessary pitfalls or future problems. If you ignored this and just used the startup action hook method, you may never have thought of or realized the need of doing any type of data cleanup upon portlet undeployment. Furthermore, it is just not possible using that method directly. By following the Liferay way, you are minimizing risk, and extending Liferay. You are not fighting against it. You are thus truly building on top of the platform.