2013-10-07

How do you deploy multiple versions of the same portlet in Liferay?

While developing a portal site, it can be very helpful to support deploying multiple versions of the same portlet.  Some of the reasons that our development team have encountered include:

  • Helping to debug issues that are introduced in new versions
  • Comparing functionality and performance between two portlets, while keeping everything else equal
  • Comparing functionality and performance between two portlets, while keeping everything else equal
  • When a portlet is used multiple times on a single site, it can be advantageous to use multiple versions of the same portlet so that all dependent portlets don't have to be updated when new features are added to the portlet that is used multiple times
  • In Liferay, JAR files are not cleaned up when a portlet is redeployed.  Therefore, if the JAR files a portlet uses are updated and it is redeployed, both the original JAR files and new JAR files will be in the lib directory.  This can cause issues if the new JAR files contain different versions of classes.
    • We have run into this situation a couple of times, and it leads to confusing and unexpected results
To properly version a portlet, you need to do two things which we achieved by adding version numbers to both:
  1. Make the directory the war file is deployed to unique so that Liferay treats them as separate portlets (in the webapps directory)
  2. Make the name of the portlet that shows up in Liferay's Add menu unique so that you can control the version of the portlet that is added to a page, and later on determine which version of the portlet is on each page
Controlling webapps directory

Through experimentation, I found that the webapps directory is based on the WAR filename.  The exact WAR filename is used as the name of the webapps directory, except when the WAR filename contains the string "-portlet".  When the WAR filename contains specific character sequences, everything after them is ignored.  The character sequences that I know about are: -portlet; -hook; -ext.  Here are a couple of examples:

WAR Filename
webapps directory
calendar_1.0.3.1.war
calendar_1.0.3.1/
calendar_1_0_4_0.war
calendar_1_0_4_0/
myportlet-portletAA.war
myportlet-portlet/
crazystuff-ext.war
crazystuff-ext/
myaccount-hook12-production.war
myaccount-hook/

Controlling the portlet name (in Liferay)

The first thing that needs to be done is to make the portlet ID unique so that Liferay can track it.  The portlet id is in liferay-display.xml.  I simply concatenate the version number onto the end of the portlet ID.

Next is to update the portlet name.  The name must be updated and kept consistent in portlet.xml and liferay-portlet.xml.  To be consistent, I simply concatenate the version number to the end of the portlet name.

One thing to keep in mind is that there is a known bug in Liferay, where an exception is thrown if the portlet name has a hyphen ('-') in it.  We also found issues with periods ('.') in the name and in the WAR filename.  So what we do is avoid using either of these characters (as well as spaces), and replace them with underscores ('_').

Maven


In our case, we are using Maven.  So to simplify portlet versioning, we keep the version number in the POM file and variables throughout the other files where necessary.  The variables are automatically replaced with the version number when the WAR file is generated.

In the POM file we setup the WAR filename to be ${pom.name}_${pom.version}.war to minimize the number of changes required when changing the name and version of the portlet or hook.  Note that Maven can't be used with ext plugins.