Now it's time to talk about a most interesting aspect of portlet technology: how portlets process a request. We will take one backward step first. In the previous article's How a Portal Page is Created section, we said that most portals are implemented as web applications, and are deployed in an application server. In the case of Pluto, the
org.apache.pluto.portalImpl.Servlet is mapped to receive all requests. So the first point of contact is a servlet that gets the browser request and generates markup.
Play little with your HelloWorld application a little bit, and you will note that whenever you are interacting with one of the portlets, the whole page gets refreshed. In other words, if you perform some action on Deploy War Admin Portlet, the complete page will get refreshed instead of only that portlet.
Let's look at an example of what happens when you're interacting with a portal. We will assume that you have two portlets, called A and B, on one page. Portlet A can get a request in either of two cases.
The user clicked a link or submitted a form in Portlet A. In this case, Portlet A should do something, like execute some business logic and generate new markup. This is called action processing because the user is performing some action on Portlet A.
The user is performing some action on Portlet B, and as a result, the complete page needs to be regenerated. Please note that when the user performs this action, the browser won't only update the content of Portlet B; instead, it will ask the portal server to regenerate the complete page. In this case, Portlet A should not execute any business logic; instead, it should regenerate markup. This is called the render phase.
Figure 2 shows how a request is handled in the portal server.
Figure 2. Portal request handling
When the user clicks a link on Portlet A, the browser will submit the request to the portlet container. The portlet container will be notified that some action was performed on Portlet A, and will call the
processAction() method of Portlet A. This is called the action processing phase. Once action processing is complete, the container will start the render phase. In this phase, the container figures out how many portlets are on the requested page. In this case, there are two portlets, A and B, so it will call the
render() method of both these portlets in different threads. Once control returns back from both these methods, it will take the content/fragment generated by them and aggregate it to build the complete page. So when the user performs action on one of the portlets, the page gets the
processAction method call and all portlets on the page get a
render method call.
What happens if user clicks the Refresh button? Or what if the user visits or returns to the page where both of these portlets are placed? The container has to generate markup for the page, but no action is performed on any of the portlets. In this case, when the container gets the request, it will skip the action phase and in the render phase it will call the
render() method of all portlets on the page.
The portlet interface has two different methods to represent two different phases of request processing.
processAction(ActionRequest actionRequest, ActionResponse actionResponse): The portlet container will call this method when some action is performed on a portlet. It will pass information submitted by the user, such as values entered in a form, in an
ActionRequestobject. You can use the
ActionResponseto redirect the user to a new page.
render(RenderRequest renderRequest, RenderResponse renderResponse): This method is similar to the
service()method in the Servlet API. It is called in the render phase, but you should not override it directly. Instead, you override one of the mode-specific methods, such as
render()method will determine the mode requested by the user and call the appropriate
doMode()method. The portlet container will pass a
RenderResponseto this method. The developer can write markup via the object returned by
RenderRequestdoes not directly get HTML form values submitted by the user--instead, it has go through
PortletSession. This is getting complicated, so we will handle it separately in the next section.