« Back

Using custom java classes in a JSP hook

General Blogs May 10, 2013 By Kan Zhang

Issue

In Liferay hook development, one common question is:

How to import/use/access custom java classes in a JSP hook?

The question can be described as:

1. Create a hook project (for example: sample-hook)

2. Create a custom class (for example: com.kzhang.CustomJavaClass) in hook project.

3. Override a Liferay portlet jsp (for example, html/portlet/blogs/view.jsp) and try to use the CustomJavaClass in the jsp. Liferay will throw "class CustomJavaClass can't be resolved to a type" exception or ClassNotFound exception.

 

Cause

The jsps in hook plugin is deployed to ROOT servlet context. for example, the html/portlet/blogs/view.jsp in hook plugin will be copied to ${container.deploy.folder}/ROOT/html/portlet/blogs/view.jsp, and the origional view.jsp will be renamed as view.portal.jsp.

The custom classes are still remains in the hook servlet context. for example, the com.kzhang.CustomJavaClass in hook plugin will be still in ${container.deploy.folder}/sample-hook/WEB-INF/classes/com/kzhang/CustomJavaClass.class.

In html/portlet/blogs/view.jsp it can not find CustomJavaClass because each servlet context can only see it's own classes and those of the parent classloaders. In our case, the html/portlet/blogs/view.jsp is in ROOT servlet context which can not see the CustomJavaClass in sample-hook servlet context

 

Solution

There are some discussions in Liferay forum:
http://www.liferay.com/community/forums/-/message_boards/message/7454307
http://www.liferay.com/community/forums/-/message_boards/message/13467179
http://www.liferay.com/community/forums/-/message_boards/message/21397246
http://www.liferay.com/community/forums/-/message_boards/message/11508053

The most common solution is to use the ext plugin instead of hook plugin.

But can we avoid using the ext plugin and leave everything as a hook?

Solution 1: Use Spring + PortletBeanLocatorUtil + JAVA reflection

Step 1. Create the custom-spring.xml in /sample-hook/docroot/WEB-INF/src/context/custom-spring.xml folder.

Step 2. In custom-spring.xml, define the bean for your custom java class:

Step 3. In web.xml, add the context-param to load the custom-spring.xml:

Step 4. In custom_jsps/html/portlet/blogs/view.jsp, use PortletBeanLocatorUtil to load the custom java class:

If your CustomJavaClass implements an interface which is accessable in ROOT servlet context, then you can cast the customJavaClass to the interface and use the class thru interface. Otherwise you will need to use JAVA reflection to dynamically invoke the methods in the custom class.

Example here.

Solution 2: Override Struts Action and call the custom classes in Struts Action.

Setp 1. In liferay-hook.xml, override the view action of blogs portlet:

Step 2. Create the CustomStrutsAction, invoke the custom class in CustomStrutsAction and set the result in to renderRequest attribute:

( Instead of result, you can also set the custom class in the renderRequest attribute and invoke it in jsp either by using it's Interface or use java reflection)

Step 3. In custom_jsps/html/portlet/blogs/view.jsp, get the result from the renderRequest attribute:

Threaded Replies Author Date
So useful! Thanks a lot for sharing! Juan Juan Fernández May 13, 2013 6:34 AM
Very Useful in practical scenarios. Rishi Dev Gupta May 13, 2013 11:22 AM
Nice solution. Peter Mesotten May 14, 2013 5:34 AM
[...] Check below link it might help you.... Anonymous May 24, 2013 1:22 AM
I'm trying to use a custom class in a jsp hook.... Jimmy Drottz June 7, 2013 9:14 AM
Hi Jimmy, Please find the example here:... Kan Zhang June 7, 2013 12:29 PM
Thanks for the sample-hook! I've deployed it to... Jimmy Drottz June 10, 2013 7:35 AM
Could you please try to change... Kan Zhang June 10, 2013 12:17 PM
That did the job! Thanks for the help sorting... Jimmy Drottz June 11, 2013 2:09 AM
First, thank you your post. It is great.... Uyen Tu August 7, 2013 12:51 AM
Thanks, this variation worked like a charm for me. Joseph Toman October 21, 2014 12:06 AM
Thanks, this variation worked like a charm for me. Joseph Toman October 21, 2014 12:06 AM
Hi Kan I found your post very interesting,... Pawel Liferay January 16, 2014 1:33 PM
I need to add another listeners in Audit... Ajay Saharan January 21, 2014 6:29 AM
[...] Q1.Only a form can be alien in Hook ... Anonymous September 12, 2014 7:07 PM

So useful! Thanks a lot for sharing!
Juan
Posted on 5/13/13 6:34 AM.
Very Useful in practical scenarios.
Posted on 5/13/13 11:22 AM.
Posted on 5/14/13 5:34 AM.
[...] Check below link it might help you. http://www.liferay.com/web/kzhang/blog/-/blogs/using-custom-java-classes-in-a-js­p-hook Thanks, Tejas Sign in to vote. Flag Please sign in to flag this as... [...] Read More
Posted on 5/24/13 1:22 AM.
I'm trying to use a custom class in a jsp hook. However, I get the following error:
org.springframework.beans.factory.NoSuchBeanDefinitionException

Do you have an example of a simple hook using this approach or do you know of any reason why the "PortletBeanLocatorUtil.locate("my-hook","MyBean")" would fail?
Posted on 6/7/13 9:14 AM.
Hi Jimmy, Please find the example here: https://docs.google.com/file/d/0B7stjZahI-VjZWhXZFNGeVlJQms/edit?usp=sharing
Posted on 6/7/13 12:29 PM in reply to Jimmy Drottz.
Thanks for the sample-hook!
I've deployed it to 6.1.1 ce ga2 and 6.1.20 ee ga2 but I get the same result as with my own hook.
Which version are you running the example on?
Posted on 6/10/13 7:35 AM in reply to Kan Zhang.
Could you please try to change "<param-name>contextConfigLocation</param-name>" to "<param-name>portalContextConfigLocation</param-name>" in your web.xml?

Thanks.
Posted on 6/10/13 12:17 PM in reply to Jimmy Drottz.
That did the job!
Thanks for the help sorting that out. Great solution.

Cheers!
Posted on 6/11/13 2:09 AM in reply to Kan Zhang.
First, thank you your post. It is great.
Second, in this case:
If your CustomJavaClass implements an interface which is accessable in ROOT servlet context, then you can cast the customJavaClass to the interface and use the class thru interface. Otherwise you will need to use JAVA reflection to dynamically invoke the methods in the custom class.

We can implement as below:
ClassLoader classLoader = PortletBeanLocatorUtil.getBeanLocator("sample-hook").getClassLoader();
Class clazz = classLoader.loadClass("com.kzhang.CustomJavaClass");
java.lang.reflect.Field field = clazz.getField("HELP_DOMAIN");
Object obj = field.get(null);
String domain = (String)obj;

In this case, HELP_DOMAIN is constant so I don't need to create object for CustomJavaClass.

With this implement, we no need create custom-spring.xml and also update web.xml

Again, thank you your help emoticon

Uyen
Posted on 8/7/13 12:51 AM.
Hi Kan

I found your post very interesting, although I am having some problems with my implementation. First of all your code works perfect! In mine there is something special. I do not use any liferay existing class (I do not extend liferay class) but I got some external library. I want to extend one class in there and use it in the jsp file.
The error message is:
21:26:07,485 ERROR [localhost-startStop-5][PortletApplicationContext:138] org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bean class [my.test.CustomComparator] not found_Offending resource: ServletContext resource [/WEB-INF/classes/context/custom-spring.xml]_Bean 'CustomComparatorClass'; nested exception is java.lang.ClassNotFoundException: my.test.CustomComparator
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bean class [my.test.CustomComparator] not found_Offending resource: ServletContext resource [/WEB-INF/classes/context/custom-spring.xml]_Bean 'CustomComparatorClass'; nested exception is java.lang.ClassNotFoundException: my.test.CustomComparator
My code looks exactly like yours (even names) except two things:
1. extend from different external class
2. in jsp file I use MyParentClass customComparator = (MyParentClass )PortletBeanLocatorUtil.locate("Hook test-hook","CustomComparatorClass");

can you please, please help me with this one ?

regards,
pawel
Posted on 1/16/14 1:33 PM.
I need to add another listeners in Audit portlet of Liferay Enterprise version.
To log the activities for some another model event. Like i have some Document type to with add and remove events. So i want to logs the information that who added that document type or remove type.

I wrote the following code in listener
================
public class DocumentAuditListener extends BaseModelListener<DocumentType> {

@Override
public void onAfterAddAssociation(Object classPK,
String associationClassName, Object associationClassPK)
throws ModelListenerException {
// TODO Auto-generated method stub
super.onAfterAddAssociation(classPK, associationClassName, associationClassPK);
}

@Override
public void onAfterCreate(DocumentType model) throws ModelListenerException {
// TODO Auto-generated method stub
try {

AuditMessage auditMessage = AuditMessageBuilder.buildAuditMessage("New Doc Created", User.class.getName(), 12122, null);

}
catch (Exception e) {
throw new ModelListenerException(e);
}
}

}

And in portal.properties

value.object.listener.com.test.documenttype.model.DocumentType=­com.liferay.portal.audit.hook.listeners.DocumentAuditListener

And put the service jar in tomcat/lib/ext

And getting following errors........................

13:39:54,994 ERROR [HotDeployUtil:114] com.liferay.portal.kernel.deploy.hot.HotDeployException: Error registering hook for audit-hook
com.liferay.portal.kernel.deploy.hot.HotDeployException: Error registering hook for audit-hook
at com.liferay.portal.kernel.deploy.hot.BaseHotDeployListener.throwHotDeployExcepti­on(BaseHotDeployListener.java:46)
at com.liferay.portal.deploy.hot.HookHotDeployListener.invokeDeploy(HookHotDeployLi­stener.java:271)
at com.liferay.portal.kernel.deploy.hot.HotDeployUtil._doFireDeployEvent(HotDeployU­til.java:111)
at com.liferay.portal.kernel.deploy.hot.HotDeployUtil._fireDeployEvent(HotDeployUti­l.java:188)
at com.liferay.portal.kernel.deploy.hot.HotDeployUtil.fireDeployEvent(HotDeployUtil­.java:40)
at com.liferay.portal.kernel.deploy.hot.HotDeployUtil$1.doPortalInit(HotDeployUtil.­java:175)
at com.liferay.portal.kernel.util.BasePortalLifecycle.portalInit(BasePortalLifecycl­e.java:42)
at com.liferay.portal.kernel.util.PortalLifecycleUtil.flushInits(PortalLifecycleUti­l.java:45)
at com.liferay.portal.servlet.MainServlet.initPlugins(MainServlet.java:792)
at com.liferay.portal.servlet.MainServlet.init(MainServlet.java:356)
at javax.servlet.GenericServlet.init(GenericServlet.java:212)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1173)
­at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:993)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4420­)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4733)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799)
­at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601)
at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:675)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:601)
at­ org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:502)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1315)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:324)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.ja­va:142)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1061)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:840)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:463)
at org.apache.catalina.core.StandardService.start(StandardService.java:525)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:754)
at org.apache.catalina.startup.Catalina.start(Catalina.java:595)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
Caused by: com.liferay.portal.kernel.bean.BeanLocatorException: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'com.mcmcg.lawfirmportal.documenttype.service.persistence.DocumentTypePersistenc­e' is defined
at com.liferay.portal.bean.BeanLocatorImpl.locate(BeanLocatorImpl.java:71)
at com.liferay.portal.kernel.bean.PortalBeanLocatorUtil.locate(PortalBeanLocatorUti­l.java:48)
at com.liferay.portal.deploy.hot.HookHotDeployListener.getPersistence(HookHotDeploy­Listener.java:1097)
at com.liferay.portal.deploy.hot.HookHotDeployListener.initModelListener(HookHotDep­loyListener.java:1442)
at com.liferay.portal.deploy.hot.HookHotDeployListener.initModelListeners(HookHotDe­ployListener.java:1473)
at com.liferay.portal.deploy.hot.HookHotDeployListener.doInvokeDeploy(HookHotDeploy­Listener.java:538)
at com.liferay.portal.deploy.hot.HookHotDeployListener.invokeDeploy(HookHotDeployLi­stener.java:268)
... 35 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'com.mcmcg.lawfirmportal.documenttype.service.persistence.DocumentTypePersistenc­e' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefi­nition(DefaultListableBeanFactory.java:527)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBean­Definition(AbstractBeanFactory.java:1083)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(Abstract­BeanFactory.java:274)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBe­anFactory.java:190)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractA­pplicationContext.java:1075)
at com.liferay.portal.bean.BeanLocatorImpl.doLocate(BeanLocatorImpl.java:113)
at com.liferay.portal.bean.BeanLocatorImpl.locate(BeanLocatorImpl.java:68)
... 41 more
Posted on 1/21/14 6:29 AM.
[...] Q1.Only a form can be alien in Hook We are perplexing to opening a category in tradition jsp offshoot LR 6.0.6. Lets contend in a arrangement impression of item publisher we wish to do some... [...] Read More
Posted on 9/12/14 7:07 PM.
Thanks, this variation worked like a charm for me.
Posted on 10/21/14 12:06 AM in reply to Uyen Tu.
Thanks, this variation worked like a charm for me.
Posted on 10/21/14 12:06 AM in reply to Uyen Tu.