The two-phase life cycle model of portlets is a strange beast to explain to web developers. The most often heard complaint is that this model is quite unnatural for web developers. Why would any web developer want to split the simple GET and POST requests into "render" and "action processing" phases so that some portal can render it correctly? On the surface, this looks like a harsh requirement that portals impose on web app developers. A few months ago, one of my colleagues tried hard to convince me (unsuccessfully, of course) that the portlet programming model is broken, and that the portal guys got it completely wrong. I heard similar stories from portlet bridge developers whose main struggle is to some how bend over backwards to get JSF, Struts, and even ASP apps fit into the two-phase lifecycle of portlets. This exercise is easier described than done.
The key problem is not with the two-phase lifecycle model, but it is way it is understood and explained. As I try to argue below, the key principle is more fundamental, and has nothing to do with portlets and portals at all. What the two-phase lifecycle model dictates is a very well-known best practice for developing any web app.
The key design guidelines for developing web apps are the following
- Keep as many resources as possible URI addressable, preferably with unique URIs
- Use method GET to access idempotent representations of resources
- Maximize use of method GET
- When you must make state changes to a resource, use method POST. However, since POST is an unsafe request (i.e. not be repeated without explicit user intervention - see URIs, Addressability, and the use of HTTP GET and POST), redirect the client to point to an idempotent representation of the resource immediately after POST. See So Many Redirects - Which One to Use? for a discussion on the various kinds of redirects and the differences between those.
These guidelines provide (a) navigation-safety, i.e., users can use browser navigation controls (back, forward, reload etc.), and (b) bookmarkability. In essence, these guidelines make web apps plain and simple for users. I should mention that bypassing these guidelines usually causes clumsy user experience involving broken back buttons, non-bookmarkable URLs, duplicate POSTs etc., and runtime issues like lack of cachability, cache corruption (particularly with non-unique URLs), more expensive state management etc.
In the figure above, note the second step. Upon processing the POST request, the web app needs to encode the state of the resource in the redirect URI, so that the user can bookmark an idempotent representation of the resource. For instance, if the POST request is meant to create a user purchase order, you may need to encode the order ID and other pertinent details into the redirect location URI. In essence, such a redirect step would convert an unsafe interaction to a "safe interaction" that is safe to navigate, is repeatable, cacheable, and bookmarkable.
Now consider the two-phase lifecycle model of portlets as specified in JSR168 and WSRP 1.0. The life cycle has two distinct phases (a) an action processing phase to make state changes, and (b) a render phase to render the current representation of a portlet. Here is the mapping of these two phases onto the above figure.
No extra terms or boxes are required for this mapping. Of course, the terms used to describe the lifecycle are slightly different:
- Action processing phase: This maps to non-idempotent operations like submitting a form via POST. In this phase you can make state changes. Just like POST, this phase is unsafe to repeat.
- Render phase: This maps to the idempotent operation like getting the current state of a resource via GET. In this phase, portlets can't reflect state changes. Portlets must be prepared to be rendered many times without causing side effects.
What ties the action processing phase to the render phase is the navigational state. This is similar to the state you would encode in a redirect location after processing a POST.
Given this analogy, the two-phase style should seem more natural for building web apps - not just portlets. As long as web apps follow the POST + redirect style for processing non-idempotent requests, mapping of such web apps into portlets should be less difficult. The portlet programming model is not alien beast - it is just a natural representation of a well-known best practice. The fact that not all web apps follow this practice is a different issue. Adding insult to this injury, some frameworks like JSF got the idempotent vs non-idempotent concepts completely wrong. But that's a story for another time.