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?).

Thursday, April 24, 2008

Servicemix Queue filled - Queue size increased

I have been using servicemix since more then 1 year.As we have seen in urlier articles that servicemix uses ActiveMQ for Message queuing. The messages sent are filled in the Queue of destined component which will be fetched later on.

When we started using Servicemix, we faced a strange problem.After running for sometimes,our application stops processing messages.When we looked in the JMS Queue, we found that after certain period the messages in the queue are not removing.So new messages can't be processed.The Queue is full and we must restart the application to get rid of this.

Then we dig into the source code of servicemix and found how queue is given size.We increased the size as per our need and the problem was solved.

Basically when message processed by the component, it will be removed from its queue.But for some reason, if message not processed or not removed from the queue then further messages will be stored in the queue and processing stops.The application seems to be running but messages are not processed.

The class org.apache.servicemix.jbi.util.BoundedLinkedQueue contains the property capacity_
Constructor of this class contains the magic which initializes the capacity of queue size.

By default it is 1024.you can change it as per your need. I mean how much messages you think will be processed after every restart.

code:
public BoundedLinkedQueue() {
this(1024);
}
public BoundedLinkedQueue(int capacity) {
if (capacity <= 0)
throw new IllegalArgumentException();
capacity_ = capacity;
putSidePutPermits_ = capacity;
head_ = new LinkedNode(null);
last_ = head_;
}
I just simply changes the 1024 to bigger number like 1024*10 etc...

Try this.may be it can solve your problem also.

And others who know about servicemix and want to share their problems and tweaks are welcome.Put your comments here i will post it.