The Model-View-Controller (MVC) pattern is one of the most commonly used patterns for designing complex web applications.
Several MVC frameworks (e.g., Struts) exist for building servlet-based web applications, but these frameworks do not support
portlet development. This article provides an overview of MVCPortlet, a framework that addresses this need.
MVCPortlet is a framework for developing JSR-168-compliant portlets using a pattern similar to MVC.
The framework provides a ControllerPortlet that receives all processAction() and render() requests. A request is delegated to a processor that implements the business logic for a specific request type. The processors may manipulate the model by directly accessing a relational database or via Java/EJBs. The view is generated by forwarding the render requests to JSPs.
Features offered by MVCPortlet framework at the time of writing this article include:
Support for the JSR 168 standard
Server- and client-side input validation
Internationalization
Extensible permissioning module for action/render type level access control
No coding required for simple database-driven portlets
Please note that this article assumes
familiarity with concepts related to portlets and the JSR 168 standard. Uninitiated readers are strongly recommended to read
at least the JSR 168 standards specification before going further.
The MVCPortlet Framework and the MVC Pattern
The main idea behind the MVC design pattern is the separation of
The system state (Model)
The mechanism used to pass on model manipulation requests (Controller)
The presentation (Views)
Decomposing the system in this fashion helps in maintaining system integrity while providing multiple ways of viewing and editing the system.
Model
The Model in an MVC-based system maintains system state and provides operations to manipulate that state. Model state is commonly stored in a relational database. Two-tier systems use database's SQL interface to manipulate the state. Most of the newer implementations create an object-oriented abstraction layer that represents the model.
The current release of MVCPortlet provides ActionProcessor and RenderProcessor implementations that perform the database operations used in a typical data-driven application. In the future we will add the ability to work with Java Beans and EJBs. In any case, larger applications should have their own business objects that implement the model independent of any external framework. In such cases, the processor classes would simply delegate the real work to these business objects.
View
The View portion of a MVCPortlet-based application is commonly constructed using JSP technology. Technically, you can also include output generated by plain HTML files or other servlets. MVCPortlet provides a number of JSP tags geared towards use of JSPs in portlet-based applications. MVCPortlet tags facilitate internationalization, form input element generation, creation of navigation links, etc. Developers can also leverage all of the features available in standard JSP technologies, but one has to operate within the constraints and guidelines imposed by the portlet API specification. For example, request parameters must not be obtained from the request object available in a JSP page. The parameters must be accessed from the RenderRequest object that is available either via the request attribute javax.portlet.renderRequest or by using a defineObjects tag that defines a renderRequest system variable.
Controller
The Controller portion of MVCPortlet, the ControllerPortlet class, handles all incoming processAction() and render() requests. In both methods, the controller selects a processor based on a request parameter named request_type. The association between request type and a processor is defined in the portlet-config.xml file, typically located at the top level in the portlet base directory. The render() method delegates incoming requests to objects implementing com.nabhinc.portlet.mvcportlet.core.RenderProcessor interface. The processAction() method delegates requests to objects implementing the com.nabhinc.portlet.mvcportlet.ActionProcessor interface. Processors return a logical name indicating the result of their execution. The configuration file maps the logical name to the JSP file responsible for generating the display for the current request type. ControllerPortlet includes content generated by this JSP file to complete the request processing.
ControllerPortlet Lifecycle/Control Flow
In most cases, developing a portlet application using MVCPortlet simply involves configuring and installing an instance of ControllerPortlet. This section presents an overview of the lifecycle of such a portlet application, including initialization, the handling of render() and processAction() methods, and portlet destruction.
Portlet Initialization
ControllerPortlet initialization mainly consists of reading portlet configuration files and constructing instances of supporting classes needed for portlet operation. ControllerPortlet expects at least one parameter, configFile, in its portlet configuration, defined in the portlet.xml file. The value of this parameter must be the context-relative path for the portlet configuration file. In addition, you can also specify the messageFile parameter. This parameter specifies the location of the file that defines internationalized message strings. If this path starts with a "/", then it is assumed to be relative to the web application root directory. Otherwise, it is assumed to be in the portlet base directory specified in the portlet configuration file.
Render Request Processing
The portlet container invokes the render() method on ControllerPortlet whenever a page containing the portlet is loaded. Figure 1 shows the high-level steps in generating markup during this method. When ControllerPortlet receives a render request, it first looks at the request type, i.e., the value of the request_type parameter. It checks if the current caller has the necessary permission to access the current request type. The MVCPortlet framework offers a very expressive permissioning mechanism that allows access control based on user IDs, client IP, roles, relationships, etc. A PortletSecurityException is thrown if the client does not have access for the current request type.
Figure 1. Steps in render request processing
If there is a RenderProcessor associated with this request type, ControllerPortlet invokes the process method on the RenderProcessor. The process method can choose to return a key that specifies the next step in business logic execution. The key can either map to an include path or to another RenderProcessor. The mapping is specified in the portlet-config.xml file for that portlet. This feature can be used to quickly assemble business functionality from a set of reusable RenderProcessor components.
If the key returned by a RenderProcessor maps to an include path, we move on to the view generation step. Portlet developers can specify a client-specific mapping for such a key. At the time of this writing, the framework allows the generation of HTML, WML, and XHTML Mobile Profile displays. Typically, these will be JSP files that generate the requested markup based on request attributes and parameters set by the RenderProcessor objects for the current request type.
Action Request Processing
Execution of the processAction() method (depicted in Figure 2) is similar to the render requests. It begins with a permission check for the current request type, identified by the request_type request parameter. If there is a form associated with the current request type, the form fields are validated. A series of ActionProcessor objects can be chained together to execute the business logic in a manner similar to the chaining of RenderRequest objects in the render() method. After the last ActionProcessor finishes its request handling, the processor result key is mapped to either a request redirect or a render type. In case of a redirect, the portlet container will redirect the current request to the specified URL. In case of a render type, the render parameter request_type is set to that value. This value will specify the request type in the subsequent render requests on the portlet showing the results of the current action.
Figure 2. Steps in action request processing
Destruction
The portlet container calls the destroy() method on a ControllerPortlet instance at the end of its lifecycle. This method in turn calls the destroy() method on all registered ActionProcessor and RenderProcessor objects. This method can be used to release any resources that are acquired by processor objects.