This article is based on Portlets in Action, to be published June 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) ebooks and pbooks. MEAPs are sold exclusively through Manning.com. All print book purchases include an ebook free of charge. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information.
also read:
- Java Tutorials
- Java EE Tutorials
- Design Patterns Tutorials
- Java File IO Tutorials
Inter-Portlet Communication
Introduction
Inter-portlet communication using PortletSession is one of the most widely used techniques since Portlet 1.0. Data stored in APPLICATION_SCOPE of PortletSession can be shared with servlets/portlets that form part of the same portlet application.
Say, we had a book catalog portlet. Because all portlets belonging to a portlet application are defined in the same portlet deployment descriptor, the first thing we need to do is define Book Catalog and Recently Added Book portlets in the same portlet.xml file. portlets belonging to a portlet application must be packaged in the same WAR file; therefore, we must create a single WAR containing the Book Catalog and Recently Added Book portlets.
CODE REFERENCE
I recommend that you import ch11_ipc_session eclipse project from the source code that accompanies portlets in Action to view the code listings presented in this section.
Defining multiple portlets in portlet deployment descriptor
You can define multiple portlets in portlet.xml by adding a <portlet> element for each portlet that belongs to the portlet application. Listing 1 shows a portlet.xml file that defines the Book Catalog and Recently Added Book portlets.
Listing 1 Portlet deployment descriptor—multiple portlets
<portlet-app....> <portlet> <portlet-name>recentBook</portlet-name> #1 <portlet-class> #2 chapter11.code.listing.base. #2 [CA]RecentlyAddedBookPortlet #2 </portlet-class> #2 <expiration-cache>0</expiration-cache> #3 <cache-scope>private</cache-scope> <resource-bundle> #4 content.Language-ext #4 </resource-bundle> #4 <portlet-info> <title>Recently Added Book</title> </portlet-info> </portlet> <portlet> <portlet-name>bookCatalog</portlet-name> #5 <portlet-class> #6 chapter11.code.listing.base.BookCatalogPortlet #6 </portlet-class> #6 .... <expiration-cache>60</expiration-cache> <cache-scope>private</cache-scope> .... <resource-bundle> #7 content.Language-ext #7 </resource-bundle> #7 <portlet-info> <title>Book Catalog</title> </portlet-info> .... </portlet> .... </portlet-app> #1 Name of Recently Added Book portlet #2 Portlet class #3 Expiration cache set to 0 #4 Resource bundle for the portlet #5 Name of Book Catalog portlet #6 Portlet class #7 Resource bundle for the portlet
At #1, the name of the Recently Added Book portlet is defined. At #2, the fully qualified class name of the Recently Added Book portlet is specified. At #3, <expiration-cache> specifies the expiration timestamp for the cached content as 0, which means that the content generated by the Recently Added Book portlet is always considered expired by the portlet container. At #4, the <resource-bundle> element defines the resource bundle used by the Recently Added Book portlet. At #5, the name of the Book Catalog portlet is defined. At #6, the fully qualified class name of the Book Catalog portlet is specified. At #7, the resource bundle used by the Book Catalog portlet is specified.
NOTE
Listing 1 shows that the Book Catalog and Recently Added Book portlets use the same resource bundle for labels/messages, which is not unusual.
In a portlet deployment descriptor, each <portlet> element contains configuration information specific to the portlet. The configuration information specified as the sub-element of the <portlet-app> element applies to the portlet application, that is, to all portlets that form part of the portlet application. For instance, the <container-runtime-option> sub-element of the <portlet-app> element applies to all portlets defined in the portlet deployment descriptor.
NOTE
portlets packaged in the same portlet application are usually closely related to each other in their functionality and mostly share a common code base.
Now that we have defined portlets in portlet.xml file, let’s look at how the Book Catalog portlet communicates with the Recently Added Book portlet using PortletSession.
Storing and receiving information from PortletSession
When a user adds a new book using the Book Catalog portlet, the Recently Added Book portlet is supposed to display the information about the newly added book. As the Recently Added Book portlet is not the target of user action, the Book Catalog portlet is responsible for communicating to the Recently Added Book portlet that a new book has been added and that it needs to regenerate its content to reflect the newly added book.
What information to communicate
We know how to pass information from the Book Catalog to the Recently Added Book portlet (which is by using PortletSession) but we don’t know what information we should pass. While developing portlets, you need to carefully choose what information you want to pass because that will affect the content generation logic of the target portlet.
NOTE
In the context of inter-portlet communication, we’ll use the term sender portlet to refer to the portlet responsible for initiating communication and receiver portlet to refer to the portlet, which is at the receiving end of the communication. Sender portlet(s) are the target of user actions and receiver portlet(s) are portlets that should update their content based on the action taken by the user on the sender portlet(s). For instance, in our sample scenario, the Book Catalog is the sender portlet and the Recently Added Book is the receiver portlet. In some inter-portlet communication scenarios, the sender may also act as the receiver and vice versa.
Let’s look at possible candidate information that can be sent to the Recently Added Book portlet:
- Complete information about the newly added book—the portlet doesn’t need to hit the catalog data store as the complete information is available in PortletSession.
- The newly added book’s ISBN number—the portlet will retrieve the book’s details from the catalog using the ISBN number available in PortletSession.
- A flag indicating that a new book was added to the catalog—the portlet uses this flag as an indicator that it needs to re-execute the logic to find the recently added book. In this case, the portlet will hit the catalog data store to find the newly added book.
You can send any of these pieces of information to the Recently Added Book portlet, but there are trade-offs for each choice. If you send complete information via PortletSession, you are overloading your session. If you are only sending the ISBN number, you get a performance trade-off because the portlet will have to make a round-trip to the catalog data store.
From the receiver portlet’s perspective, the communicated information can be classified as shown in table 1.
In most scenarios, the partial information approach is used because the receiver portlet usually needs to process the communicated information to generate its content.
TIP
You should avoid putting information in PortletSession during the render phase. When an action method is invoked in response to a user action on the sender portlet, you should only set the session attributes. The sequence of the render method invocations for portlets in a portal page is undefined. Therefore, if you put information in PortletSession in the render method, the other portlet(s) may not be able to read it because their render methods were already invoked by the time the information was put in PortletSession.
In inter-portlet communication scenarios, it is equally important to consider situations in which the communicated information is not available or was not delivered to the receiver portlet—the No information case defined earlier. The receiver portlet should show meaningful content even if the communicated information hasn’t been delivered or is unavailable. For instance, if the Recently Added Book portlet is completely dependent on the ISBN number stored in PortletSession for generating its content, it will not show any book details until the user adds a new book to the catalog. This gives the impression that no book has ever been added to the catalog and the catalog should be empty.
NOTE
Because we are not using a database for the examples in this article, the Book Catalog is stored as a ServletContext attribute named bookCatalog by BookCatalogContextListener (a servlet context listener). Refer to the web.xml file and the BookCatalogContextListener class in ch11_ipc_session source folder that accompanies Portlets in Action.
Caching and inter-portlet communication
In inter-portlet communication scenarios, content caching in the receiver portlet can be a spoilsport. This is how: if the receiver portlet caches the content based on the expiration-based or validation-based caching strategy, the content of the receiver portlet is not updated until the content expires or becomes invalid. So, even if you pass information via PortletSession to the receiver portlet, it will not result in updated portlet content until the cached content expires and the receiver portlet’s render method is invoked.
In our sample inter-portlet communication scenario, the Book Catalog portlet communicates the ISBN number of the newly added book to the Recently Added Book portlet via PortletSession. The content generation logic of the Recently Added Book portlet is responsible for showing meaningful content if the ISBN number of the newly added book is not available in PortletSession.
Listing 2 shows the addBook method of the BookCatalogPortlet class, which adds the ISBN number of the newly added book to PortletSession.
Listing 2 BookCatalogPortlet class—addBook method
@ProcessAction(name = "addBookAction") public void addBook(ActionRequest request, #1 ActionResponse response)throws.... { #1 .... if (errorMap.isEmpty()) { #2 bookService.addBook(new Book(category, name, #3 author, Long.valueOf(isbnNumber))); #3 request.getPortletSession().setAttribute #4 ("recentBookIsbn", isbnNumber, #4 PortletSession.APPLICATION_SCOPE); #4 .... } }
At #1, the addBook method of the BookCatalogPortlet class is responsible for adding a book to the catalog. At #2, the addBook method checks if errorMap is empty. errorMap is a HashMap, which contains validation errors occurred during the validation of the user-defined book information. If errorMap is empty (that is, no validation errors occurred), the book information is saved in the catalog by calling the addBook method of BookService, at #3. At #4, the ISBN number of the newly added book is set in the APPLICATION_SCOPE of PortletSession with name recentBookIsbn.
The ISBN number information stored in the PortletSession is now accessible to Recently Added Book portlet. Listing 3 below shows how the RecentlyAddedBookPortlet class retrieves the ISBN number from PortletSession and uses it to generate its content. If the ISBN number is found in the session, then it sorts books available in the catalog to find the recently added book.
Listing 3 RecentlyAddedBookPortlet class
public class RecentlyAddedBookPortlet extends GenericPortlet { @RenderMode(name="view") public void showRecentBook(RenderRequest request, #1 RenderResponse response)throws ....{ #1 String isbnNumber = (String)request. #2 getPortletSession().getAttribute("recentBookIsbn", #2 PortletSession.APPLICATION_SCOPE); #2 .... if(isbnNumber != null) { if(bookService.isRecentBook (Long.valueOf(isbnNumber))) { book = bookService.getBook(Long. #3 valueOf(isbnNumber)); #3 } else { book = bookService.getRecentBook(); #4 } } else { book = bookService.getRecentBook(); #5 } request.setAttribute("book", book); #6 getPortletContext().getRequestDispatcher( #7 response.encodeURL(Constants.PATH_TO_JSP_PAGE + #7 "recentPortletHome.jsp")).include(....); #7 } } #1 Render method for VIEW mode #2 Retrieve ISBN number from PortletSession #3 Retrieve book details using ISBN number #4 Retrieve recent book from catalog #5 Retrieve recent book from catalog #6 Generate portlet content using JSP page
At #1, showRecentBook defines the render method for the VIEW portlet mode. At #2, showRecentBook method retrieves the recentBookIsbn session attribute from APPLICATION_SCOPE. The recentBookIsbn attribute contains the ISBN number of the recently added book set by the Book Catalog portlet (refer to listing 2). At #3, if the ISBN number stored in the recentBookIsbn session attribute represents a recently added book in the catalog, the getBook method of BookService is called to obtain the details of the book whose ISBN number matches the value of the recentBookIsbn session attribute. At #4, if the ISBN number stored in the recentBookIsbn session attribute doesn’t represent a recently added book in the catalog, getRecentBook method of BookService is used to retrieve the recently added book from the catalog. At #5, if the recentBookIsbn session attribute is not found, the getRecentBook method of BookService is used to retrieve the recently added book from the catalog. At #6, the Book object returned by the getBook/getRecentBook method is set as a request attribute for use by the JSP page responsible for rendering content. At #7, a request is dispatched to the recentPortletHome.jsp page of the Recently Added Book portlet to show the details of the recently added book.
Listing 3 shows that the RecentlyAddedBookPortlet class does a lot of work to ensure that the content is meaningful in the context of user action. For instance, if it doesn’t find the recentBookIsbn session attribute or if it finds the ISBN number referenced by the recentBookIsbn session attribute doesn’t represent a recent book in the catalog then it executes a complex logic within the getRecentBook method to fetch the newly added book.
CODE REFERENCE
The getRecentBook method of BookService is responsible for fetching fresh catalog data and sorting it based on a sequence number assigned to each book in the catalog. The book with the highest sequence number is considered as the recently added book in the catalog.
We’ve just seen in listings 2 and 3 how PortletSession is used by the Book Catalog and Recently Added Book portlets to implement inter-portlet communication. Pretty neat, huh? Now we are ready to look at how to build, deploy, and test inter-portlet communication between the Book Catalog and Recently Added Book portlets.
Inter-portlet communication in action
Earlier we saw how to write portlets that communicate using PortletSession. Now, we’ll see how our Book Catalog and Recently Added Book portlets communicate when deployed in a web portal.
Let’s look at the steps that we need to take to test communication between our sample portlets.
Build and deploy portlet application WAR file
The steps to build and deploy portlet application containing multiple portlets are the same as for the portlet application containing a single portlet.
Create portal pages
Because we want to test inter-portlet communication in situations when communicating portlets are located on different portal pages, you should create two portal pages named Book Catalog and Recent Book.
Communicating portlets on the same portal page
To test inter-portlet communication, add the Book Catalog and Recently Added Book portlets to the Book Catalog portal page, as shown in figure 1.
Figure 1 shows communicating portlets on the same portal page. To test the communication between the sample portlets, add a new book to the catalog. You’ll discover that the Recently Added Book portlet updates its content to reflect the new book.
Communicating portlets on different portal pages
To test the communication between the sample portlets when they are located on different portal pages, add the Book Catalog portlet to the Book Catalog portal page and the Recently Added Book portlet to Recent Book. Now, add a new book to the catalog and check if the Recently Added Book portlet content reflects the details of the newly added book. You’ll find that the Recently Added Book portlet does show information about the newly added book.
So, now you’ve seen just how easy it is to get your portlets talking to each other, whether they’re on the same portal page or different ones. Next, we’ll take a look at the pros and cons of using PortletSession for inter-portlet communication.
Advantages and disadvantages of using PortletSession
Even though PortletSession was a preferred way to achieve inter-portlet communication in Portlet 1.0 days, it still has many advantages in Portlet 2.0 world.
Table 2 describes the advantages and disadvantages of using PortletSession as the means for communication between portlets.
Table 2 describes the pros and cons of using PortletSession for inter-portlet communication, which you should consider before deciding the approach that you want to use for communication between portlets.
NOTE
Some portal servers provide additional features to support the sharing session attributes between the portlets in different portlet applications. For instance, you can share the APPLICATION_SCOPE session attributes with portlets in other portlet applications using the session.shared.attributes property in the portal-ext.properties file. Note that such features are portal specific in nature and will result in portability issues.
Summary
In this article, we discussed how PortletSession, public render parameters, and portlet events can be used by portlets to coordinate with other portlets in the web portal. It is hardly possible to create a web portal with portlets that work in silos; therefore, it is important to carefully choose the inter-portlet mechanism that fits your web portal requirements. You may use three inter-portlet communication approaches in your web portal depending on the communication needs of the portlets. One of the biggest advantages of having portlets that communicate with each other is that such communication enriches the users’ experience of the web portal. Another advantage is that, at any given time, portlets reflect the content that is relevant in the context of user actions.