Tuesday, September 28, 2010

Liferay 6- Scheduler Engine design change


In Liferay 5.2.x, we had com.liferay.portal.kernel.job.IntervalJob as a Quartz scheduler job, which needs to be implemented for different jobs we need for Quartz trigger execution.

In Liferay 6, the design pf scheduler engine has been changed and introduced combination with JMS. I have prepared the small class cum collaboration diagram, which can help understanding how we need to make triggers now onwards.


Basically, they made use of messaging with Quartz. The Job class for liferay is now fixed which is com.liferay.portal.scheduler.job.MessageSenderJob .


Now we have a com.liferay.portal.kernel.messaging.MessageListener which is to be implemented for different job handling.

The schedulerEnginUtil class will register the listener with perticular queue destination and schedules new trigger as per the SchedulerEntry details with MessageSenderJob. When trigger fired, this Job class will eventually sends message to queue, which will eventually go to the listener.

This time, not as like previous scheduler, they have given exposure to Message which can contain some payload data if needed for job execution. This is very helpful sometimes.

By default the method schedule(SchedulerEntry schedulerEntry, ClassLoader classLoader)from SchedulerEngineUtil sends null Message. So, we might need to copy and pass some payload object if needed.

Tuesday, September 7, 2010

Liferay Struts Portlet: Usage of SessionErrors class...

This is very common issues we come across when we want to change portal code with extension environment. Mostly we do is, copying the code from portal to ext and making new struts portlet.

And for sending errors to pages, we use SessionErrors.add(...) method, which will eventually forward to default error.jsp file. So you will never find what is happening. Simple thing, to make this working fine, is...

- Make new empty page named error.jsp in your portlet.
- Make new Struts-config and tiles-def entry of your new JSP in ext.
- From your render method, forward to the new file when you have SessionErrors filled with errors like,

 if (!SessionErrors.isEmpty(req)) {
  return mapping.findForward("portlet.portlet_name.error");
 }
 

See this link to understand the detailed problem...

Tuesday, August 31, 2010

Liferay: Exposing Webservice & access using Axis Client...

Basically in liferay, we create services using service builder, which is like child's play. In service.xml, we need to put local and remote both attributes, and it will generate class for web service also. 

The link shows steps,
- To generate web services using service builder.
- To generate WSDD changes.
- To access web service from Axis client

Friday, August 20, 2010

Liferay Wrapper Plugins ...

Very nice article about liferay core functionality extension. You can create wrappers around core services and it will be in service when you compile-deploy it! Isn't that's great? Check the link.

Tuesday, August 17, 2010

Liferay: Make Custom "Add Application" portlet ...

Sometimes client will amaze us by giving very very complex requirements which not seem to be a value adding thing. But still we need to complete it as it's our work :). Same happened here, when i need to make a portlet replicating the "Add Application" liferay portlet.

Though its not new, it has given me chance to see how "Add Application" menu works.

Basically liferay uses a Javascript file named layout_configuration.js to call the layout_configuration portlet.

Liferay has in-built portlet named layout_configuration for this functionality. It renders the same using LayoutConfiguration.toggle('87'); where 87 is the portlet ID.

Find the following portlet in file portal\portal-web\docroot\WEB-INF\liferay-portlet.xml,

 <portlet>  
         <portlet-name>87</portlet-name>  
         <icon>/html/icons/default.png</icon>  
         <struts-path>layout_configuration</struts-path>  
         <use-default-template>false</use-default-template>  
         <show-portlet-access-denied>false</show-portlet-access-denied>  
         <show-portlet-inactive>false</show-portlet-inactive>  
         <restore-current-view>false</restore-current-view>  
         <private-request-attributes>false</private-request-attributes>  
         <private-session-attributes>false</private-session-attributes>  
         <render-weight>50</render-weight>  
         <system>true</system>  
     </portlet>  

Also find the portlet in file \portal\portal-web\docroot\WEB-INF\portlet-custom.xml as below,

 <portlet>  
         <portlet-name>87</portlet-name>  
         <display-name>Layout Configuration</display-name>  
         <portlet-class>com.liferay.portlet.StrutsPortlet</portlet-class>  
         <init-param>  
             <name>view-action</name>  
             <value>/layout_configuration/view</value>  
         </init-param>  
         <expiration-cache>0</expiration-cache>  
         <supports>  
             <mime-type>text/html</mime-type>  
         </supports>  
         <resource-bundle>com.liferay.portlet.StrutsResourceBundle</resource-bundle>  
     </portlet>  

You will find the path layout_configuration in files struts-config.xml and tiles-defs.xml in the same folder.

In struts-config,

     <!-- Layout Configuration -->  
     <action path="/layout_configuration/templates" forward="portlet.layout_configuration.templates" />  
     <action path="/layout_configuration/view" forward="portlet.layout_configuration.view" />  

In tiles-defs,

 <!-- Layout Configuration -->  
 <definition name="portlet.layout_configuration" extends="portlet">  
     <put name="portlet_decorate" value="false" />  
 </definition>  
 <definition name="portlet.layout_configuration.templates" path="/portlet/layout_configuration/templates.jsp" />  
 <definition name="portlet.layout_configuration.view" extends="portlet.layout_configuration">  
     <put name="portlet_content" value="/portlet/layout_configuration/view.jsp" />  
 </definition>  

At first I started learning what liferay is doing. Basically in this portlet, liferay generates the HTML layout of all portlets in liferay-display.xml as per the category. It makes the whole tree structure of categories and subcategories. This portlet is not described in liferay-disaply.xml, so it will not be disaplayed in the tree.

Then it loads this portlet on onclick event of "Add Application" menu click. It calls the portal servlet at url /portal/render_portlet, which will render the portlet and shows the result HTML in popup dialog.

Check the javascript function from layout_configuration.js file,

 toggle: function(ppid) {  
         var instance = this;  
         var plid = themeDisplay.getPlid();  
         var doAsUserId = themeDisplay.getDoAsUserIdEncoded();  
         if (!instance.menu) {  
             var url = themeDisplay.getPathMain() + '/portal/render_portlet';  
             var popupWidth = 250;  
             var body = jQuery('body');  
             body.addClass('lfr-has-sidebar');  
             instance._dialog = Liferay.Popup(  
                 {  
                     width: popupWidth,  
                     message: '<div class="loading-animation" />',  
                     position: [5,5],  
                     resizable: false,  
                     title: Liferay.Language.get("add-application"),  
                     onClose: function() {  
                         instance.menu = null;  
                         body.removeClass('lfr-has-sidebar');  
                     }  
                 }  
             );  
             jQuery.ajax(  
                 {  
                     url: url,  
                     data: {  
                         p_l_id: plid,  
                         p_p_id: ppid,  
                         p_p_state: 'exclusive',  
                         doAsUserId: doAsUserId  
                     },  
                     success: function(message) {  
                         instance._dialog.html(message);  
                         instance._loadContent();  
                     }  
                 }  
             );  
         }  
     }  

After that, the javascript adds click and drag-drop even for all portlet divs of that popup dialog. So that user will be able to add the portlet into page by click OR drag-drop.

I need to show list of portlets of specified categories only without the categories.So i need to copy this portlet as it is and filter as per the category.

First of all i choosen the plugins environment for my new portlet, but it failed because i need to use the static resource named WebAppPool as per the code below,

PortletCategory portletCategory = (PortletCategory)WebAppPool.get(String.valueOf(company.getCompanyId()), WebKeys.PORTLET_CATEGORY);

This pool is maintained by liferay, it has all the categories on server startup. Now liferay loads the war file of plugin portlet when we add the portlet in page. So new classloader will be used here. And in java, child classloader can't access parent classloader's resources.

So i decided to move to ext environment. I made the new "Custom Add Application" portlet in ext with some code changes.

1. Need to filter the categories, for that i used portlet's init-param to define my including categories as below,

 <init-param>  
         <name>includeCategories</name>  
         <value>myCategory,yours</value> <!-- Comma seperated category names, sub-categories will not be filtered...-->  
     </init-param>  

2. Changed the code in view.jsp, included the categories only given in the init-param of above step and will show in HTML un-ordered list. Sent all categories to view_category.jsp.

3. Changed the code in view_category.jsp, displayed the portlets from all categories of above step as HTML list-items. It only shows portlets, ignoring the categories from view.

4. Same JS file has been used with different name to keep the drag-drop support for portlet list-items. Changed the JS file accordingly. Like ID for the result element is passed because now we don't want it to be in popup dialog etc...

5. Made new theme from classic, and changed the navigation.vm to show the above portlet as navigation menu item.

Though this was not extra-ordinary development, i got the chance to look into some internals of Liferay's odd style of developing a complex product, which may give us harsh times if touched :) (Isn't its too complex for a portal?).