
Access Liferay API from Portlet
Table of Contents [-]
The problem #
Starting from Liferay 4, a new separate classloader architecture is used. This brings lots of improvements such as the possibility of the portlet using its own version of the libraries such as hibernate, spring, etc. that are also used by the core Liferay code.
On the other hand, it might also have negative side effects to portlets using the API offered by Liferay from a portlet application packaged in a WAR. If that's your case the recommended solution is to change your code to use the portal-service.jar API.
But sometimes that solution is not applicable such as when:
- You need a very quick solution until you change your code
- You are accesing internal APIs that are not being exposed through liferay-client
In those cases you can use the following solution.
The solution #
The solution is to make all of Liferay's classes accessible through the global classpath. There are three ways to achieve this:
1. Make the portlet application use the portal webapps classloader. In tomcat you achieve this by creating a file called context.xml in the META-INF folder of your portlet webapp with the following content:
<Context> <Loader loaderClass="com.liferay.support.tomcat.loader.PortalClassLoader" /> </Context>
2. Configure the application server to use a single classloader for all application.
3. Copy all of Liferay's libraries and it's dependent libraries to a path in the global classpath.
The next sections offer detailed instructions for alternative 3 for the most common application servers.
Note: This solution has an important drawback: as we are putting all the libraries that are used by Liferay in the global classpath it might collide with other versions of the same libraries in independent portlet applications. If you run into this problem use the recommended solution of invoking only the public Liferay API offered through liferay-service.jar.
Tomcat #
In tomcat the best solution is to copy the necessary libraries to the global classpath. Here are the recommended steps to do this:
0) Stop Tomcat
1) Check that the directory common/lib/ext exists. If it does not exist create it and add it to the list of global classpath paths editing $TOMCAT_HOME/conf/catalina.properties and adding it to the values of common.loader:
common.loader= ${catalina.home}/common/classes,\ ...\ ${catalina.home}/common/lib/ext/*.jar
2) Move all files from ROOT/WEB-INF/lib to the common/lib/ext. Be sure to not leave any library in the original location
3) Start Tomcat
JBoss-Tomcat #
For JBoss it's possible to configure a single classloaders:
1) Open $JBOSS_HOME/server/default/deploy/jbossweb-tomcat55.sar/META-INF/jboss-service.xml and set UseJBossWebLoader to true:
<attribute name="UseJBossWebLoader">true</attribute>
With this setting we use Jboss' unified classloader instead of tomcat's classloader.
2) Open $JBOSS_HOME/server/default/conf/jboss-service.xml and set the deployment sorter to PrefixDeplomentSorter instead of the default org.jboss.deployment.DeploymentSorter:
<attribute name="URLComparator">org.jboss.deployment.scanner.PrefixDeploymentSorter</attribute>
This way we'll be able to control the order in which the web applications are loaded by changing the WAR file names. Our purpose will be to make sure that the portal loads first that the portlet applications.
3) Rename the file names of all portlet applications to make them start with the number '1'. For example:
sample-struts-portlet.war -> 1sample-struts-portlet.war sample-jsp-portlet.war -> 1sample-jsp-portlet.war
Drawbacks and alternatives #
The main drawback of this solution is that it will not be possible to have different versions of the same library in two different portlet applications. Also those libraries already included in Liferay such as Spring, Struts, Apache Commons, etc must be used by the portlet applications which won't be able to provide their own.
The only alternative to this solution is to use the liferay-client.jar API.