Chapter 11. Communicating with other portlets

This chapter covers

  • Inter-portlet communication using portlet sessions
  • Public render parameters
  • Portlet events
  • Portlet event support in Spring Portlet MVC
  • Pros and cons of different approaches to inter-portlet communication

In 2003, when Portlet 1.0 (JSR 168) was released, it didn’t define support for communication between portlets. The only way to achieve inter-portlet communication in the Portlet 1.0 days was by using PortletSession, which required communicating portlets to reside in the same portlet application. If you wanted a third-party portlet to communicate with your custom portlet, you had to package them as part of a single portlet application.

The lack of inter-portlet communication support also led to using portal server–specific approaches to achieve communication between portlets, resulting in portability issues. This limitation in Portlet 1.0 made inter-portlet communication one of the highly anticipated features in Portlet 2.0.

There are many ways in which your portlets can communicate with each other; this chapter focuses on inter-portlet communication mechanisms that are primarily used in portals and that are supported by Portlet 2.0–compliant containers. We’ll also look at how the inter-portlet communication approaches compare with each other, and the scenarios in which it makes sense to use one mechanism over another. We’ll first look at how portlets can communicate using PortletSession, and then we’ll move on to inter-portlet communication mechanisms available in Portlet 2.0–compliant containers.

Let’s look at an example that demonstrates the importance of inter-portlet communication.

11.1. Why do you need inter-portlet communication?

Inter-portlet communication is a process in which an action taken on a portlet is communicated to other portlets in the web portal, giving them an opportunity to update their content if it’s relevant in the context of the user action.

Inter-portlet communication is central to portlets for two reasons:

  • It makes portlets reusable in different scenarios.
  • It helps portlets that aren’t the target of a particular user action provide relevant information based on the user action.

Imagine a portal page in a car manufacturing firm’s web portal that consists of an Order portlet and an Inventory portlet. The Order portlet shows the current status of all the orders in the system, and the Inventory portlet shows the current inventory status for all the cars manufactured by the firm. The Order and Inventory portlets get their content from the order and inventory management systems, respectively. A user can log in to the web portal and can view an order in the Order portlet. When the user selects an order, the Inventory portlet should fetch the inventory of cars that are related to that order, to show their availability.

In this scenario, the Order portlet must communicate which cars are in the order so that the Inventory portlet can display information for those cars. Figure 11.1 shows how the Order and Inventory portlets communicate with each other and with their source systems.

Figure 11.1. The Order and Inventory portlets communicate with their respective source systems and with each other.

The user selects an order from the Order portlet. The order details are fetched from the order management system, and the Order portlet communicates a list of cars to the Inventory portlet. The Inventory portlet fetches the inventory status for those cars from the inventory management system.

In Figure 11.1, if it wasn’t possible for the Order portlet to communicate with the Inventory portlet, the Order portlet would need to interact with the inventory management system to fetch the inventory status for the cars, making the portlet dependent upon the inventory management system for its content. If you then needed to display the price for each of the cars, based on information from a pricing management system, the Order portlet would become dependent upon yet another system.

Even though the pricing, order, and inventory management systems are distinct systems, the Order portlet would become dependent on all three to generate its content.

This approach would defeat the main purpose of using portlets, because the Order portlet would be responsible for consolidating information from distinct sources. The Order portlet doesn’t qualify to be called a mashup portlet (a portlet that provides a new service based on existing services) because it isn’t providing any new service to the user. It’s simply gathering data from distinct sources to generate its content.

If the Order portlet is directly dependent on other sources of information, this dependency affects its reusability in different scenarios. For instance, if the Order portlet needs to be used in the web portal developed specifically for the manufacturing firm’s marketing team to analyze demand for cars in different geographical regions, then it can’t be reused because of its dependence on inventory and pricing management systems.

To avoid these problems, we need some mechanism that allows portlets to coordinate with each other to break down the silo behavior. This will result in a dynamic web portal that presents content that’s relevant based on user actions. The result will be an enriched user experience.

Before we learn how to implement inter-portlet communication, let’s take a look at the requirements of a web portal which will be used as the basis for implementing different inter-portlet communication mechanisms in this chapter.

11.2. An inter-portlet communication example

In previous chapters, you saw that the Book Catalog portlet is used to add books to and remove them from the catalog. Suppose you need to develop a new portlet named Recently Added Book that shows the book most recently added to the catalog. When a book is added to the catalog, the Recently Added Book portlet updates its content to reflect the details of the recently added book. Let’s see how this will work when the Book Catalog and Recently Added Book portlets are deployed in a web portal.

Figure 11.2 shows the Add Book form of the Book Catalog portlet, which allows you to add a new book to the catalog.

Figure 11.2. The Add Book form of the Book Catalog portlet, for adding books to the catalog

When a book is successfully added to the catalog, the Recently Added Book portlet updates its content to show the details of the recently added book, as shown in Figure 11.3.

Figure 11.3. The Recently Added Book portlet is updated every time a book is added to the catalog.

 

Note

It’s expected that the Book Catalog and Recently Added Book portlets are on the same portal page, but they could very well be on separate portal pages.

 

Looks pretty simple, right? The coordination between the Book Catalog and Recently Added Book portlets can be achieved in a number of ways, such as by using a database, the portlet context, Ajax (discussed in chapter 12), and other techniques. In this chapter we’ll focus on the following mechanisms, which are predominantly used in inter-portlet communication:

  • Portlet session
  • Public render parameters
  • Portlet events

Table 11.1 identifies the scenarios best suited for using each of these approaches.

Table 11.1. Inter-portlet communication mechanisms and their preferred uses

Inter-portlet communication mechanism

When to use

Portlet session If your portlets are part of the same portlet application, or if your portal server supports sharing session data between portlets in different portlet applications
Public render parameters If your portlets communicate using simple string values and are in different portlet applications
Portlet events If your portlets communicate with each other by sending or receiving complex objects and they’re in different portlet applications

In the following sections, we’ll look at each of these approaches in detail.

11.3. Inter-portlet communication using portlet sessions

Inter-portlet communication using PortletSession is one of the most commonly used techniques since Portlet 1.0. In this section, we’ll explore PortletSession-based inter-portlet communication in the context of the Book Catalog and Recently Added Book portlets. We’ll also look at how content caching can affect PortletSession-based communication and how you can make use of portal server–specific features to make inter-portlet communication possible between portlets in different portlet applications.

Let’s see how the Book Catalog and Recently Added Book portlets can be set up to be part of the same portlet application so that they can communicate using portlet sessions. As we discussed in chapter 3, data stored in the PortletSession’s APPLICATION_SCOPE can be shared with servlets and portlets that form part of the same portlet application. All portlets belonging to a portlet application are defined in the same portlet deployment descriptor, so the first thing you need to do is to define the 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, so you must create a single WAR containing the two portlets.

 

Code Reference

You should now import the ch11_ipc_session Eclipse project from the source code that accompanies this book, in order to view the code listings presented in this section.

 

11.3.1. Defining multiple portlets in the portlet deployment descriptor

You can define multiple portlets in portlet.xml by adding a <portlet> element for each portlet in the portlet application. The following listing shows the portlet.xml file from the ch11_ipc_session project, which defines the Book Catalog and Recently Added Book portlets.

Listing 11.1. Defining multiple portlets in the portlet deployment descriptor

The name and fully qualified class name of the Recently Added Book portlet is specified . <expiration-cache> specifies the expiration time 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. The <resource-bundle> element defines the resource bundle used by the Recently Added Book portlet .

The name and fully qualified class name of the Book Catalog portlet is specified , and the resource bundle is specified .

 

Note

Listing 11.1 shows that the Book Catalog and Recently Added Book portlets use the same resource bundle for labels and messages, which isn’t unusual.

 

In a portlet deployment descriptor, each <portlet> element contains configuration information specific to that portlet. The configuration information specified as a subelement of the <portlet-app> element applies to all the portlets in the portlet application. For instance, the <container-runtime-option> subelement of the <portlet-app> element applies to all of the 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 you’ve defined the portlets in the portlet.xml file, let’s look at how the Book Catalog portlet communicates with the Recently Added Book portlet using PortletSession.

11.3.2. Storing and retrieving information from PortletSession

When a user adds a new book with the Book Catalog portlet, the Recently Added Book portlet is supposed to display the information about the newly added book. Because the Recently Added Book portlet isn’t the target of a user action, the Book Catalog portlet is responsible for informing the Recently Added Book portlet that a new book has been added, and that it needs to regenerate its content.

Determining What Information to Communicate

You know how to pass information from the Book Catalog to the Recently Added Book portlet (by using PortletSession) but what information should you 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 refer to the portlet responsible for initiating communication as the sender portlet, and the portlet at the receiving end of the communication will be the receiver portlet. Sender portlets are targets of user actions, and receiver portlets are the ones that should update their content based on the action taken by the user in the sender portlets. In our example, the Book Catalog is the sender portlet and Recently Added Book is the receiver portlet. In some inter-portlet communication scenarios, a sender may also act as a receiver, and vice versa.

 

Let’s look at the possible information that could be sent to the Recently Added Book portlet:

  • Complete information about the newly added book— The Recently Added Book portlet wouldn’t need to hit the catalog data store because the complete information would be available in PortletSession.
  • ISBN of the newly added book— The Recently Added Book portlet would retrieve book details from the catalog using the ISBN available in PortletSession.
  • A flag indicating that a new book was added to the catalog— The Recently Added Book portlet would use this flag as an indicator that it needs to re-execute the logic to find the most recently added book.

You could send any of the preceding information to the Recently Added Book portlet, but there are trade-offs with each approach. If you send the complete information via PortletSession, you’re overloading your session; if you send just the ISBN, there’s a performance trade-off because the portlet will have to make a round trip to the catalog data store.

From the perspective of the receiver portlet, the communicated information can be classified as shown in table 11.2.

Table 11.2. Classification of information received by a portlet

Information received

Description

Complete In this case, the receiver portlet doesn’t need to do anything extra; the communicated information is sufficient for the receiver portlet to generate its content.
Partial In this case, the receiver portlet processes the partial information received from the sender portlet to generate its content.
No information In this case, the receiver portlet receives no information about the content that it needs to generate.

In most scenarios, the partial information approach is used; the receiver portlet usually needs to process the communicated information to generate its content.

 

Note

As recommended in chapter 3, you should avoid putting information in PortletSession during the render phase. Only when an action method is invoked in response to a user action on the sender portlet should you set the session attributes. As mentioned in chapter 2, the sequence of render method invocations for portlets in a portal page is undefined; if you put information in a PortletSession in the render method, the other portlets may not be able to read it because their render methods will have already been invoked by the time the information was put in PortletSession.

 

In inter-portlet communication scenarios, it’s also important to consider situations in which the communicated information isn’t available to the receiver portlet or was not delivered—the no-information case in table 11.2. The receiver portlet should show meaningful content even if the information that should have been communicated wasn’t delivered or is unavailable. For instance, if the Recently Added Book portlet is completely dependent on the ISBN stored in PortletSession for generating its content, it won’t show any book details until a user adds a new book to the catalog. This gives the impression that no book has ever been added to the catalog and that the catalog is empty.

 

Note

Because we aren’t using a database for the examples in this chapter, 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 the ch11_ipc_session source folder that accompanies this book.

 

Caching and Inter-Portlet Communication

In inter-portlet communication scenarios, content caching in the receiver portlet can be a spoilsport. For example, if the receiver portlet caches content based on an expiration-based or validation-based caching strategy, the content of the receiver portlet isn’t updated until the content expires or becomes invalid. Even if you pass information via PortletSession to the receiver portlet, the portlet’s content won’t be updated until the cached content expires and the render method of the receiver portlet is invoked.

In our example, the Book Catalog portlet communicates the ISBN of the newly added book to the Recently Added Book portlet via PortletSession. The Recently Added Book portlet’s content generation logic is responsible for showing meaningful content if the ISBN of the newly added book isn’t available in PortletSession.

This listing shows the addBook action method of the BookCatalogPortlet class, which adds the ISBN of the newly added book to PortletSession.

Listing 11.2. The BookCatalogPortlet class’s addBook method

The BookCatalogPortlet class’s addBook method is responsible for adding a book to the catalog. The addBook method checks whether errorMap is empty; errorMap is a HashMap that contains validation errors that occurred when the book information entered by the user was validated. If errorMap is empty (meaning that no validation errors occurred), the book information is saved in the catalog by calling the BookService’s addBook method. The ISBN of the newly added book is then set in the APPLICATION_SCOPE of PortletSession with the name recentBookIsbn.

The ISBN stored in PortletSession is now accessible to the Recently Added Book portlet. The following listing shows how the RecentlyAddedBookPortlet class retrieves the ISBN from PortletSession and uses it to generate its content. If an ISBN is found in the session, the RecentlyAddedBookPortlet uses the BookService class to retrieve the recently added book.

Listing 11.3. The RecentlyAddedBookPortlet class

The showRecentBook method represents the render method for VIEW portlet mode. The showRecentBook method retrieves the recentBookIsbn session attribute from APPLICATION_SCOPE. The recentBookIsbn attribute contains the ISBN of the recently added book, set by the Book Catalog portlet (in listing 11.2).

If the ISBN stored in the recentBookIsbn session attribute represents a recently added book in the catalog, the BookService’s getBook method is called to retrieve the details of the book whose ISBN matches the value of recentBookIsbn. If the ISBN stored in the recentBookIsbn session attribute doesn’t represent a recently added book in the catalog, or the recentBookIsbn session attribute isn’t found, the BookService’s getRecentBook method is used to retrieve the most recently added book in the catalog.

The Book object returned by the getBook or getRecentBook method is set as a request attribute for use by the JSP page responsible for rendering content. The request is then dispatched to the recentPortletHome.jsp page of the Recently Added Book portlet to display the details of the recently added book.

Listing 11.3 shows that the RecentlyAddedBookPortlet class does a lot of work to ensure that the content is meaningful. For instance, if it doesn’t find the recentBookIsbn session attribute, or if it finds that the ISBN referenced by the recentBookIsbn session attribute doesn’t represent the most recently added book in the catalog, it executes some complex logic within the BookService’s getRecentBook method to fetch the most recently added book.

 

Code Reference

The BookService’s getRecentBook method 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 to be the most recently added book in the catalog. Refer to the BookServiceImpl class in the ch11_ipc_session source folder for more details.

 

You’ve seen in listings 11.2 and 11.3 how PortletSession is used to implement inter-portlet communication. Now it’s time to look at how you can build, deploy, and test this inter-portlet communication between the Book Catalog and Recently Added Book portlets.

11.3.3. Inter-portlet communication in action

In the previous section, you saw how to write portlets that communicate using PortletSession. Now you’ll see how the 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 example portlets.

1.  Build and deploy a portlet application WAR file.

The steps involved in building and deploying a portlet application containing multiple portlets are the same as for a portlet application containing a single portlet. Refer to chapter 1 for a discussion of how to build and deploy portlets in Liferay Portal.

2.  Create portal pages.

Because you want to test inter-portlet communication in situations when the communicating portlets are located on different portal pages, you should create two portal pages named Book Catalog and Recent Book. Refer to chapter 1 for the steps required to create portal pages in Liferay Portal.

3.  Test inter-portlet communication between 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 11.4. Refer to chapter 1 for the steps required to add portlets to a portal page with Liferay Portal.

Figure 11.4. The Book Catalog and Recently Added Book portlets are on the same portal page. The Recently Added Book portlet shows information when a new book is added using the Book Catalog portlet.

Figure 11.4 shows communicating portlets on the same portal page. To test communication between the portlets, add a new book to the catalog, as shown in Figure 11.2. You’ll find that the Recently Added Book portlet updates its content to reflect the new book, as shown in Figure 11.3.

4.  Test inter-portlet communication between portlets on different portal pages.

To test the communication between portlets when they’re located on different portal pages, add the Book Catalog portlet to the Book Catalog portal page and the Recently Added Book portlet to the Recent Book page. Now add a new book to the catalog and check whether the Recently Added Book portlet’s content reflects the details of the newly added book. You’ll find that the Recently Added Book portlet does show the new information.

You’ve now seen 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 look at the pros and cons of using PortletSession for inter-portlet communication.

11.3.4. Advantages and disadvantages of using PortletSession

PortletSession was the preferred way to achieve inter-portlet communication in the Portlet 1.0 days, and it still has many advantages in the Portlet 2.0 world. These are the advantages:

  • The sender portlet can pass any object type to the receiver portlet.
  • Portlets that are part of the same portlet application have access to the common PortletSession, so they can easily take up the role of sender or receiver portlet without needing any configuration changes.
  • Portlets can communicate with each other using PortletSession even when they’re on different portal pages.

And here are the disadvantages of using PortletSession for inter-portlet communication:

  • Portlets must be packaged as part of the same portlet application to take advantage of PortletSession for communication. This means it isn’t possible for a portlet to communicate with a third-party portlet.
  • Web components that are part of the same portlet application can access and modify data stored in PortletSession, which can result in unpredictable outcomes.

You should consider these pros and cons before deciding on the approach you want to use for communication between portlets.

 

Note

Some portal servers provide additional features for sharing session attributes between portlets in different portlet applications. For instance, in Liferay Portal you can share APPLICATION_SCOPE session attributes with portlets in other portlet applications by using the session.shared.attributes property in the portal-ext.properties file (discussed in appendix B) and the <private-session-attributes> element of the liferay-portlet.xml file. Such features are portal-specific and will result in portability issues.

 

Let’s now look at how public render parameters are used in inter-portlet communication.

11.4. Inter-portlet communication using public render parameters

Request parameters received in the portlet’s render method are referred to as render parameters. Render parameters are meant to communicate information that’s required by the portlet to generate appropriate content.

There are two types of render parameters:

  • Private render parameters— These request parameters aren’t visible to other portlets.
  • Public render parameters— These request parameters are visible to other portlets on the same portal page.

So far in this book, you’ve seen that render parameters are received by a portlet in the following situations:

  • When a form is submitted to a portlet’s render URL, the form fields are sent as render parameters to the render method.
  • When a hyperlink that references a portlet’s render URL is clicked, the parameters set in the render URL are sent as render parameters to the render method.
  • When a render parameter is set in the ActionResponse using the setRenderParameter method, the portlet container makes the render parameter available to the following render method call.

The render parameters used in these situations are private render parameters because they’re not shared with other portlets. Each portlet has its own set of private render parameters that are made available to the portlet when its render method is invoked by the portlet container. Public render parameters, on the other hand, are available to all portlets in the same or different portlet applications.

 

Warning

You should not submit a form to a portlet’s render URL because form submissions are associated with updating system state, which isn’t recommended in the render phase. For instance, in the Book Catalog portlet, you shouldn’t submit the Add Book form to the render URL.

 

The primary purpose of both render parameter types is to provide information that portlets can use in generating their content. For instance, the render method of the Book Catalog portlet in chapter 6 uses the myaction private render parameter to decide on the JSP to be used for generating portlet content.

Let’s look at how you can set or retrieve public render parameters in portlets.

11.4.1. Setting and retrieving public render parameters

If you take a quick look at the Portlet 2.0 API in chapters 3 and 4, you’ll find that the PortletResponse interface and its subinterfaces don’t define a setter method for public render parameters. So how do you set a public render parameter if there’s no method available to set it? You can set public render parameters using the setter method for private render parameters. Sound misleading?

Public render parameters can be thought of as an extension to the concept of private render parameters. To create public render parameters, you just need to tell the portlet container to treat certain private render parameters as public render parameters. All you need to do is specify the names of the private render parameters that you want to be treated as public render parameters in the portlet.xml file.

The only thing that changes when a private render parameter is converted to a public render parameter is its scope. Like private render parameters, public render parameters are available to the portlet in the render method; additionally they’re available in action, resource, and event methods. The scope of public render parameters goes beyond the current portlet, so they’re also available in the lifecycle methods of other portlets in the same or different portlet applications.

Don’t worry if you find the concept of private and public render parameters confusing; I’ll come back to them later in this chapter to clarify the difference. For now, you can assume that they’re mostly the same. Private render parameters are set in the action or event method and are available only in the render method. Public render parameters, unlike private render parameters, are also available to other portlets and in all lifecycle methods.

Let’s look at how you can use public render parameters in the Book Catalog and Recently Added Book portlets we’re discussing in this chapter.

Public Render Parameters and Inter-Portlet Communication

As discussed in section 11.3, the Book Catalog portlet should communicate the ISBN of the recently added book to the Recently Added Book portlet via the public render parameter. Figure 11.5 shows how public render parameters are used by portlets for communication.

Figure 11.5. Public render parameters are set as if they were private render parameters. The portlet deployment descriptor is responsible for distinguishing public render parameters from private ones.

The user performs an action on the sender portlet. The sender portlet uses the ActionResponse’s setRenderParameter method to set a private render parameter named x. If this private render parameter is specified as a public render parameter in portlet.xml, it will be available to the receiver portlet . The updated content of the receiver portlet is shown to the user.

 

Code Reference

You should now import the ch11_BookCatalog_public and ch11_RecentBook_public Eclipse projects from the source code that accompanies this book, so you can view the code listings presented in this section. The ch11_BookCatalog_public project contains the Book Catalog portlet and ch11_RecentBook_public contains the Recently Added Book portlet.

 

Setting Public Render Parameters in the Portlet Deployment Descriptor

The distinction between public and private render parameters is made by the portlet deployment descriptor. Both the sender and receiver portlets must define the render parameters shared by the sender portlet. We’ll revisit this concept after discussing the portlet deployment descriptors of the sender and receiver portlets.

The next listing shows the portlet deployment descriptor of the Book Catalog portlet—the sender portlet in our example.

Listing 11.4. The Book Catalog portlet’s portlet.xml file

Listing 11.4 shows how the <supported-public-render-parameter> and <public-render-parameter> elements are used to transform the recentBookIsbn private render parameter into a public render parameter named myBookISBN in the namespace http://www.mynamespace.com/.

The Book Catalog portlet’s name and class are specified. The <supported-public-render-parameter> specifies the render parameter name that the bookCatalog portlet retrieves or sets in its portlet class. Because it will be shared with other portlets, it’s defined in the portlet deployment descriptor using the <supported-public-render-parameter> element. The recentBookIsbn render parameter name specified in the <supported-public-render-parameter> must uniquely identify a corresponding <public-render-parameter> element.

The <public-render-parameter> element maps the render parameter used internally by the Book Catalog portlet to the qualified name by which it’s known to other portlets in the portal. The value of the <identifier> element specifies the render parameter’s internal name, recentBookIsbn, defined by the <supported-public-render-parameter> element. The <qname> element specifies the namespace-qualified name by which the render parameter is accessible to other portlets: the namespace is http://www.mynamespace.com/ and the local name is myBookISBN.

Now, other portlets can access the recentBookIsbn render parameter using the qualified name with which it was exposed.

The following listing shows the portlet deployment descriptor of the Recently Added Book portlet.

Listing 11.5. The Recently Added Book portlet’s portlet.xml file

The Recently Added Book portlet’s name and class are specified . The <supported-public-render-parameter> element specifies the render parameter that the recent-Book portlet retrieves or sets in its portlet class. The myCustomRecentBookIsbnParamName render parameter is mapped to the qualified name by which it’s known to other portlets in the portal.

 

Warning

Up to Liferay Portal 5.2.3, the value of the <identifier> element must be the same in all portlets that communicate using public render parameters. To address this issue, the Recently Added Book portlet’s portlet.xml file uses recentBookIsbn as the value of the identifier element instead of myCustomRecentBookIsbnParamName. If you’re using Liferay Portal 6.x or GlassFish with OpenPortal Portlet Container 2.1.2, this warning doesn’t apply.

 

If you compare listing 11.4 with listing 11.5, you can see that the qualified name for the recentBookIsbn render parameter is the same as for the myCustomRecentBookIsbnParamName parameter. This means that they are essentially the same render parameters; any change in the Book Catalog portlet’s recentBookIsbn render parameter will be reflected in the Recently Added Book portlet’s myCustomRecentBookIsbnParamName render parameter, and vice versa.

Figure 11.6 summarizes how the Book Catalog portlet communicates with the Recently Added Book portlet using public render parameters. This figure shows that public render parameters can be thought of as variables stored in a global namespace that’s accessible to other portlets in the portal. You can define a namespace of your choice to qualify your public render parameter’s local name. For instance, the http://www.mynamespace.com/ namespace represents a fictitious namespace in this example.

Figure 11.6. The Book Catalog and Recently Added Book portlets communicate with each other using a render parameter that maps to the myBookISBN name in the http://www.mynamespace.com/ namespace.

The Book Catalog portlet exposes the recentBookIsbn render parameter as the myBookISBN variable in the http://www.mynamespace.com/ namespace. The Recently Added Book portlet exposes the myCustomRecentBookIsbnParamName render parameter as the myBookISBN variable in the http://www.mynamespace.com/ namespace. myBookISBN represents a global variable to which both the recentBookIsbn and myCustomRecentBookIsbnParamName render parameters refer. At , http://www.mynamespace.com/ represents a container for the myBookISBN variable.

 

Warning

Because we aren’t using a database to demonstrate the examples in this chapter, there are some limitations on how the book catalog data is shared between the Book Catalog and Recently Added Book portlets. To keep things simple, the Recently Added Book portlet uses a ServletContext attribute to store some book catalog data, which contains books with the following ISBNs: 1, 2, 3, 4, and 5. When you add a book in the Book Catalog portlet, the ISBN of the newly added book is made available to the Recently Added Book portlet as a public render parameter. To see inter-portlet communication in action between the two portlets, you must add a book in the Book Catalog portlet with an ISBN of 1, 2, 3, 4, or 5.

 

If you want to avoid defining a namespace for each of the public render parameters, you can define a default namespace for all public render parameters defined in the portlet application by using the <default-namespace> element. The next listing shows the Book Catalog portlet’s deployment descriptor from listing 11.4, rewritten using the <default-namespace> element.

Listing 11.6. A portlet.xml using the default-namespace element

The <default-namespace> element defines a default namespace for all the public render parameters defined using <public-render-parameter> elements. The <name> subelement of the <public-render-parameter> element is used to specify the local name of the public render parameter instead of using the <qname> subelement. In this case, the default namespace is implicitly associated with the unqualified local name of the public render parameter.

11.4.2. Methods related to public render parameters

As was mentioned earlier, the Portlet 2.0 API doesn’t define any methods to explicitly set public render parameters; they’re set the same way private render parameters are. But the API does provide methods to retrieve public render parameters explicitly from portlet requests.

You can retrieve a public render parameter by using any of the following PortletRequest methods:

  • getPublicParameterMap()—Returns public render parameters as a java.util.Map object. The name of the parameter is the key, and a String[] represents its value.
  • getParameter(String name)—Returns the public render parameter’s value. The public render parameter name is identified by the name argument.
  • getParameterValues(String name)—Returns a String[] of the public render parameter’s values. The public render parameter name is identified by the name argument.

 

Note

You can use the PortletConfig object’s getPublicRenderParameterNames method to get the list of public render parameters supported by a portlet.

 

When it comes to removing a private render parameter, the Portlet API doesn’t provide any methods to do this. But you can remove a private render parameter by setting its value to null. On the other hand, public render parameters can be removed using the StateAwareResponse interface’s removePublicRenderParameter method. Because it’s in the StateAwareResponse interface, this method only allows you to remove public render parameters in action and event methods.

The following code snippet shows how you can remove the recentBookIsbn public render parameter defined in listing 11.6 using the removePublicRenderParameter method:

response.removePublicRenderParameter("recentBookIsbn");

Here, response represents the ActionResponse or EventResponse object.

The use of the recentBookIsbn name in the preceding method is consistent with using the internal name of a public render parameter in the portlet code, and not the name given in the global namespace.

 

Note

Public render parameters don’t have a well-defined lifecycle like private render parameters do. Private render parameters are available during the render phase, and then they’re destroyed by the portlet container. Public render parameters are available until you explicitly remove them using the removePublicRenderParameter method of the ActionResponse or EventResponse object.

 

Now that you’ve seen how public render parameters are used in inter-portlet communication, let’s discuss the advantages and disadvantages of using public render parameters.

11.4.3. Advantages and disadvantages of using public render parameters

As you saw, using public render parameters for inter-portlet communication is as simple as adding some configuration information to the portlet deployment descriptor. Here are some more benefits of using public render parameters:

  • It’s the simplest approach to achieve inter-portlet communication. This approach doesn’t require code changes, and it gives you the flexibility to expose additional render parameters when required.
  • The communicating portlets need not be in the same portlet application.
  • If the portal server stores render parameters in the URL, it’s easy to bookmark the URL to the portal page. When you visit the portal page, the portlets will use public render parameters to generate relevant content. Suppose a Flight Search web portal page contains Flight and Location portlets. When you select a city from the Location portlet, the Flight portlet displays inbound and outbound flights for that city, based on a ZIPCode public render parameter. If the portal server stores the ZIPCode public render parameter in the URL, the user can bookmark this URL, and when they use the bookmarked URL later, the Flight portlet will use the same ZIPCode public render parameter value to generate content for the same city. This saves the user the effort of reselecting the appropriate city from the Location portlet to view flights in the Flight portlet.
  • Public render parameters don’t impact the performance of the web portal. There’s no extra processing required to pass public render parameters to other portlets, so there’s no performance impact on the web portal. (In the next section, we’ll discuss event-based inter-portlet communication, which does require extra processing on behalf of the portlet container.)

 

Warning

Liferay Portal and Jetspeed portal servers don’t store public render parameters in the URL, so you won’t be able to take advantage of bookmarking URLs with public render parameters.

 

Public render parameters have their disadvantages too, which need to be considered before deciding to use them for inter-portlet communication:

  • Render parameters (whether private or public) can only have String or String[] as their value type. If you’re using PortletSession or event-based portlet communication, you can pass complex objects between communicating portlets. In the case of action and resource requests, public render parameters are merged with the action and resource parameters, respectively. If an action or resource parameter has the same name as the public render parameter, the public render parameter values appear towards the end of the parameter value array.
  • Because public render parameters are available to all portlets in the portal page and to other web components, they can be removed or modified by other components.
  • Public render parameters are available to portlets on the same portal page, but not to portlets on different portal pages. Some portal servers, like WebSphere Portal, allow portlets to access public render parameters on different portal pages.
  • If the receiver portlet uses validation-based or expiration-based caching and it needs to update its content based on the values of a public render parameter, its content won’t be updated until the cache expires. If you want to view the updated content immediately after the public render parameters are set by the sender portlet, you should set the <expiration-cache> to 0 in the receiver portlet’s definition in portlet.xml.

Now that you know about the pros and cons, it’s time to see public render parameters used in inter-portlet communication. Let’s look at an example.

11.4.4. Putting public render parameters to work

The source code for the Book Catalog and Recently Added Book portlets is located in the ch11_BookCatalog_public and ch11_RecentBook_public source folders. The example portlets have been kept in separate portlet applications to show how public render parameters can be used for communication between portlets in different portlet applications.

Figure 11.7 shows how these two portlets communicate using public render parameters.

Figure 11.7. The Recently Added Book and Book Catalog portlets get book data from their respective ServletContexts. In a real-world situation, both portlets would obtain book data from a shared database.

A book is added to the Book Catalog portlet , with an ISBN value from 1 to 5. The book data is saved in the ServletContext of the portlet application that contains the Book Catalog portlet . The ISBN of the newly added book is made available to the Recently Added Book portlet as a public render parameter. The ISBN public render parameter is retrieved by the Recently Added Book portlet to obtain the book information from the ServletContext of the portlet application that contains the Recently Added Book portlet.

Let’s now look at how portlet events can be used to achieve inter-portlet communication.

11.5. Inter-portlet communication using portlet events

Portlet 2.0 introduced the concept of portlet events to allow a loosely coupled approach to inter-portlet communication that is tightly integrated with the portlet lifecycle. You can think of portlet events as being like emails. You send email using your favorite email client, and the delivery is taken care of by a mail server. In the case of portlet events, the portlet sends the events, and the portlet container is responsible for making the event available to the receiver portlets.

In this section, we’ll take an in-depth look at how portlet events are used for inter-portlet communication. You’ll see how portlets can communicate with each other using portlet events, using the Book Catalog and Recently Added Book portlets as examples. Toward the end of this section, we’ll also look at how Spring Portlet MVC controllers support the processing of portlet events.

When you use public render parameters or the portlet session for inter-portlet communication, it’s your responsibility to retrieve the communicated information in the receiver portlet’s render method (or in an appropriate lifecycle method) and to process the communicated information. When you use portlet events, the sender portlet uses events to communicate the information to the receiver portlets. The portlet container acts as the broker, delivering the events to the portlets that define an interest in receiving the event.

As mentioned in the previous sections, if you’re using PortletSession or public render parameters for inter-portlet communication, you need to be aware of the following limitations:

  • You shouldn’t perform updates in the receiver portlet during the processing of the communicated information because application state shouldn’t be updated during the render phase.
  • If the receiver portlet caches its content, it won’t generate fresh content until the cache is expired by the portlet container.

On the other hand, if you’re using portlet events for inter-portlet communication, the receiver portlet can update application state, and the portlet container is responsible for expiring the cached content of the portlet, giving it the opportunity to generate fresh content in response to event processing.

Figure 11.8 shows how the event lifecycle phase fits into the portlet’s request-processing lifecycle.

Figure 11.8. Portlet C processes the event generated by portlet A in the event phase. The portlet container processes generated events before invoking the render method of the portlets on the portal page.

The action request for portlet A is received by the portal server. Portlet A generates an event named e during action request processing. Portlet C processes the event e generated by portlet A. The render methods of portlets A, B, and C are invoked by the portlet container. The updated content of portlet C is shown to the web portal user , because the portlet container expires the content of portlets that are the target of an event.

Let’s now look at how events are sent and received by portlets.

11.5.1. Sending portlet events

To send events to other portlets, a portlet must do the following:

  • Set the events that the portlet wants to publish to other portlets
  • Define, in portlet.xml, the events that the portlet wants to publish

Let’s look at both of these tasks in detail.

 

Code Reference

You should now import the ch11_BookCatalog_event and ch11_RecentBook_event Eclipse projects from the source code that accompanies this book, so you can view the code listings presented in this section. The ch11_BookCatalog_event project contains the Book Catalog portlet, and the ch11_RecentBook_event project contains the Recently Added Book portlet.

 

Setting Events For Publishing

A portlet can set events that it wants to send to other portlets by using the following two methods of the StateAwareResponse interface:

  • setEvent(QName qname, Serializable value)—The qname argument, of type javax.xml.namespace.QName, represents the namespace-qualified name of the event. The value argument, of type java.io.Serializable, represents the data (also referred as the payload) that’s associated with the event.
  • setEvent(String name, Serializable value)—The name argument represents the local name of the event. The value argument represents the event’s payload.

Because the setEvent methods are defined in StateAwareResponse, which is the superinterface of the ActionResponse and EventResponse interfaces, you can only send events in the action or event phases.

 

Note

You earlier saw how the <qname> subelement of the public-render-parameter element is used to specify the qualified name of the public render parameter in the portlet deployment descriptor. The QName class provides an object representation of a qualified name.

 

The next listing shows how the BookCatalogPortlet class sets the bookAddedEvent event when a new book is successfully added to the catalog.

Listing 11.7. The addBook method of the BookCatalogPortlet class

The addBook action method is responsible for adding a new book to the catalog. The addBook method creates a qualified name for the event that the portlet wants to publish to other portlets. This qualified name consists of two parts: the namespace (http://www.mynamespace.com) and the local name (bookAddedEvent).

The setEvent method sets the event for publishing by the portlet container. BookAddedEvent represents the payload or data associated with the event, which is a newly added book in this case.

In listing 11.7, the setEvent method sets the bookAddedEvent event that the Book Catalog portlet wants to publish to other portlets. The event is published to interested portlets by the portlet container after the addBook action method completes, but before the rendering of portlets begins.

The following listing shows the BookAddedEvent class, which is used as a payload for the bookAddedEvent event.

Listing 11.8. The BookAddedEvent event payload

The @XmlRootElement class-level annotation of JAXB (Java Architecture for XML Binding) indicates that BookAddedEvent acts as the root element of the generated XML document . The BookAddedEvent class implements the java.io.Serializable interface . BookAddedEvent defines a no-argument constructor .

 

Note

In the context of event-based inter-portlet communication, we’ll refer to the event-publishing portlet as the sender portlet and the event-processing portlet as the receiver portlet.

 

You might be wondering why the BookAddedEvent event payload class implements the Serializable interface and defines a no-argument constructor. As I explained earlier, the portlet container plays the role of event broker and is responsible for delivering events to portlets. If a portlet wants to send events to a portlet located on a remote portlet container, the portlet container will need to serialize the event payload to send it across the network. The remote portlet container will then need to deserialize the event payload to make it available to the receiver portlets. To support inter-portlet communication with remote portlets, portlet containers use JAXB to serialize the event payload (a Java object) set by the sender portlet to XML, and then to deserialize the XML back to the event payload and to make it available to the receiver portlet. JAXB doesn’t know how to instantiate the BookAddedEvent class, so you need to provide a no-argument constructor.

It’s important to note that if sender and receiver portlets are located in the same Java runtime, the portlet container can use the Java serialization mechanism to pass the event payload or it can pass a reference to the event payload object to the receiver portlet. Your receiver portlet implementation should be independent of the mechanism (JAXB, Java serialization, or object reference) used by the portlet container.

 

Note

The Book Catalog portlet in the current example sends the complete details of the newly added book as the event payload, to demonstrate that the event payload can be a complex object and not just a String object.

 

When BookAddedEvent is set by the Book Catalog portlet, it’s serialized to an XML document by the portlet container, as shown here:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bookAddedEvent>
    <author>Ramnivas Laddad</author>
    <category>Java</category>
    <isbnNumber>1933988053</isbnNumber>
    <name>AspectJ in Action, Second Edition</name>
</bookAddedEvent>

The preceding XML fragment shows that the properties of BookAddedEvent were converted to subelements of bookAddedEvent.

 

Note

You don’t need to learn JAXB in depth unless your portlet events represent a complex object structure consisting of custom Java objects, for which JAXB doesn’t provide default bindings. For more details on JAXB, see the JAXB website (http://jaxb.dev.java.net).

 

Configuring Glassfish With Openportal Portlet Container

If you’re using Java SE 6, you don’t need to include the jaxb-api JAR file to compile the source code of the ch11_BookCatalog_event and ch11_RecentBook_event projects because JAXB is included with Java SE 6.

If you’re using Java SE 5, you do need the jaxb-api JAR file to build the projects. If you’re using Java SE 5 to run the examples, you must have jaxb-api, stax-api, and the stax implementation JAR files in the server classpath, as described in section 1.7.1 of chapter 1.

Configuring Liferay Portal 5.2.3

If you’re using Liferay Portal 5.2.3 (and not Liferay Portal 6.x), you need to add the JAR file containing your event class, and any classes that it depends on, to your server’s classpath. The BookAddedEvent that’s sent from the Book Catalog portlet to the Recently Added Book portlet depends on the Book class, so the BookAddedEvent and Book classes must be packaged in a JAR file and put in the server classpath.

You can create a JAR file from the source code of the ch11_BookCatalog_event or ch11_RecentBook_event projects by using the Eclipse IDE’s JAR export utility. If you’re using Liferay Portal bundled with Tomcat, you can add the generated JAR file to the TOMCAT_HOME/lib/ext folder and add it to the server classpath, as described in section 1.7.1 of chapter 1.

 

Note

A bookAddedEvent.jar JAR file is already provided in the event-jar sub-folder of ch11_BookCatalog_event.

 

Setting events in the ActionResponse or EventResponse object is the necessary first step in sending events to other portlets. You also need to define, in the portlet deployment descriptor, the events that a portlet publishes.

Defining Events in the Portlet Deployment Descriptor

To publish an event, you must do the following:

  • Define the event at the portlet application level.
  • Define support for the event in portlets that publish it.

The next listing shows how the Book Catalog portlet defines the bookAddedEvent in portlet.xml.

Listing 11.9. Defining an event in the Book Catalog portlet’s portlet.xml file

The Book Catalog portlet name and class are specified . The <event-definition> subelement of the <portlet-app> element defines the event that one or more portlets in the portlet application can publish or process.

The <event-definition> element is specified as follows:

  • The <qname> element specifies the qualified name of the event: bookAddedEvent is the local name, and http://www.mynamespace.com is the namespace.
  • The <value-type> element specifies the fully qualified name of the class that acts as the event payload.

If you want to use a default namespace for the events defined in portlet.xml, you should use the <default-namespace> subelement of <portlet-app>. If you are using <default-namespace>, instead of <qname> you should use the <name> element to specify a local name for the event.

The <supported-publishing-event> subelement of the <portlet> element defines support for the bookAddedEvent event. In <supported-publishing-event>, you can identify an event using either a qualified name or a local name. The <qname> element has been used here because <event-definition> defines the bookAddedEvent event with a qualified name. Alternatively, you can use the <name> element to identify an event with a local name.

 

Note

You can use the getPublishingEventQNames method of the PortletConfig object to obtain the QNames of the events published by a portlet.

 

This is all you need to do to send portlet events. You may have gotten a déjà vu feeling while going through the event definition in portlet.xml, because events and public render parameters are defined similarly. Now let’s look at how portlets can receive events published by other portlets.

11.5.2. Receiving portlet events

To receive portlet events, a portlet must do the following:

  • The portlet must define in portlet.xml the events that it can process.
  • The portlet implementation class must implement the EventPortlet interface.
Defining Events that a Portlet Can Process

In listing 11.7 you saw how the Book Catalog portlet sets the bookAddedEvent portlet event. Listing 11.9 showed how that event is exposed to other portlets by defining it in portlet.xml. As the Recently Added Book portlet is interested in bookAddedEvent, it defines support for processing the event, as shown next.

Listing 11.10. Defining support for processing an event in the portlet.xml file

The Recently Added Book portlet name and class are specified . The <supported-processing-event> element specifies that the Recently Added Book portlet supports processing for the bookAddedEvent event. <event-definition> specifies the bookAddedEvent event and its payload object .

 

Note

Portlets that publish events use the <supported-publishing-event> element to specify the event that they publish, and portlets that consume or receive or process events use the <supported-processing-event> element to specify the events that they can process. A portlet can use both of these elements to specify events it publishes and consumes. You can obtain the QNames of the events that a portlet can process using the PortletConfig object’s getProcessingEventQNames method.

 

Now that you’ve defined support for the bookAddedEvent event, let’s see how the event is processed by the portlet.

Implementing the EventPortlet Interface

You saw in chapter 2 that the Portlet interface provides render and processAction lifecycle methods, which handle render and action requests respectively. Event processing is an optional feature for portlets, so it’s defined in a separate interface, EventPortlet.

The EventPortlet interface defines a single method that has the following signature:

void processEvent(EventRequest request, EventResponse response)
 throws PortletException, java.io.IOException

If your portlet is to receive events from other portlets, your portlet class must implement the EventPortlet interface, directly or indirectly. The portlet’s processEvent method is executed when events for the portlet are received by the portlet container.

 

Note

Events aren’t delivered immediately to other portlets when you set them in your portlet class (see listing 11.7). The portlet container records all the events set by the sender portlets and delivers them to the receiver portlets before initiating the render phase for all the portlets on the portal page.

 

If your portlet class extends the GenericPortlet class, you don’t need to worry about implementing the EventPortlet interface, because GenericPortlet already implements it. The default implementation of the processEvent method in GenericPortlet attempts to invoke the @ProcessEvent annotated method defined for the received event. The following listing shows the @ProcessEvent annotated event for the Recently Added Book portlet.

Listing 11.11. The process event in the RecentlyAddedBookPortlet class

The @ProcessEvent annotation specifies that the processAddedBookEvent method processes the bookAddedEvent. You can specify the qname or name element in the @ProcessEvent annotation: the qname element is used to identify the event using its QName (see listing 11.7); the name element is used to identify the event using its local name. Listing 11.11 uses the qname element because QName was used to define the event, as shown in listings 11.7, 11.9, and 11.10. The value of the qname element has the following format:

{<namespace-uri>}<local-name>

Here, namespace-uri identifies the namespace URI associated with the event, and local-name identifies the local name of the event. The processAddedEvent is the event processing method, which accepts EventRequest and EventResponse objects as arguments.

The getEvent method of EventRequest returns the Event object associated with the incoming event. The getValue method of the Event object returns the pay-load object associated with the event, which is the BookAddedEvent object in this case.

The book details are retrieved from the BookAddedEvent object and are set as render parameters in EventResponse.

 

Warning

The order in which events are executed isn’t necessarily the order in which they’re set by the portlet. The portlet container may reorder the events before delivering them to the receiver portlets.

 

One of the interesting things to notice in listing 11.11 is the use of the setRenderParameter method to set render parameters in EventResponse. You’ve seen in earlier chapters that render parameters are set in the action method and are available in the following render method. Similarly, render parameters set in the event method are available in the following render method. Because the event method is invoked after the action method, it’s possible to replace or remove the render parameters set in the action method.

Let’s now look at the pros and cons of using portlet events in inter-portlet communication.

11.5.3. Advantages and disadvantages of using portlet events

Portlet events offer a sophisticated form of event-driven portlet communication that’s coupled with the portlet lifecycle.

These are the advantages of using portlet events for communication:

  • Portlet events are associated with a payload object, which is useful in communicating complex information. Public render parameters can only send String values to receiver portlets.
  • The portlet container removes (or expires) the cached content of the portlet that is the target of a portlet event. This feature isn’t available for public render parameters. Content expiration guarantees that the render method of the receiver portlet is invoked and fresh content is generated.
  • You can create event chains. If portlet A processes event X, then it can generate another event Y in the event method. Event Y is processed by portlet B, which generates event Z in its event method. This can go on and can be used to create an interconnected network of portlets that are affected by the single event X.

These are some disadvantages of using portlet events for communication:

  • Portlet event processing puts an extra strain on the portlet container, impacting the performance of the web portal. For simple cases, it’s preferable to use public render parameters.
  • You can’t make an existing portlet publish or process events, because it requires writing event processing code or setting portlet events. When using public render parameters, you can make portlets communicate with each other without writing any code.
  • A portlet must be the target of action processing to generate an event in the first place, but if your web portal stores public render parameters in the URL, you can have all the portlets on the portal page show relevant content without any user actions.
  • Portlet events aren’t guaranteed to be delivered to the target portlets, so receiver portlets should behave appropriately even when the events aren’t received.

So far in this chapter, we’ve discussed different approaches for implementing inter-portlet communication. Remember those tough chapters on Spring Portlet MVC? Let’s take a quick look at how you can implement inter-portlet communication when using the Spring Portlet MVC framework.

11.5.4. Event support in Spring Portlet MVC

In chapter 7, you saw that you can create Spring controllers by implementing the org.springframework.web.portlet.mvc.Controller interface. The Controller interface doesn’t define any methods for processing events because, as mentioned earlier, it’s an optional feature in portlets. But the Spring Portlet MVC’s EventAware-Controller provides event processing capabilities to controllers. It defines only one method with the following signature:

void handleEventRequest(javax.portlet.EventRequest request,
                        javax.portlet.EventResponse response)
                        throws Exception

You can see that the handleEventRequest method is similar to the processEvent method defined in the javax.portlet.EventPortlet interface of the Portlet 2.0 API.

If you’re using annotated controllers, you can annotate any method with the @EventMapping annotation to specify that the method is meant for processing events. That wasn’t so hard, was it?

11.6. Summary

In this chapter, we discussed how portlets can use PortletSession, public render parameters, and portlet events to coordinate with other portlets in the web portal. It’s hardly possible to create a web portal with portlets that work in silos, so it’s important to carefully choose the inter-portlet mechanism that fits your web portal requirements. You can end up using all three inter-portlet communication approaches in your web portal, depending upon the communication needs of your portlets. One of the biggest advantages of having portlets that communicate with each other is that it enriches the user experience of the web portal; at any given time, the portlets reflect content that’s relevant in the context of user actions.

As mentioned at the beginning of this chapter, there are multiple ways in which portlets can communicate with each other; in chapter 12 we’ll look at how portlets can communicate using the Comet (or Ajax Push) mechanism.

So far in this book, you’ve seen examples in which you upload a book’s TOC into a database or to a file folder. In the next chapter, we’ll explore the ResourceServingPortlet interface of Portlet 2.0 and see how it can be used to download resources in portlets.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.119.106