Skip to main content

Streamline Your Portlet Development with MVCPortlet Framework

March 8, 2005

{cs.r.title}









Contents
The MVCPortlet Framework and the MVC Pattern
   Model
   View
   Controller
ControllerPortlet Lifecycle/Control Flow
   Portlet Initialization
   Render Request Processing
   Action Request Processing
   Destruction
Portlet Development Example
   Defining Render Types
   Defining Action Types
   Setting Up Access Control
   Configuring RenderProcessors and ActionProcessors
   Forms and Input Validation
   Writing JSP Files
   Portlet Deployment
Resources

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
  • Device-specific display generation (XHTML/WAP/HTML)
  • Tag library for navigation, form elements
  • Support for seamless file uploads

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
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
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.







Portlet Development Example

To illustrate the portlet development process, we consider the steps involved in creating the FAQ portlet included in MVCPortlet distribution. Figure 3 shows the home page for the FAQ portlet in VIEW mode. The home page shows a list of FAQ categories. When the user clicks on a category, questions and answers belonging to the category are displayed, as shown in Figure 4.

Figure 3
Figure 3. FAQ portlet home page (VIEW mode)

Figure 4
Figure 4. FAQ portlet questions page (VIEW mode)

Defining Render Types

The first step in portlet development is to figure out the portlet modes and render types to be supported by the portlet. In this case, our portlet needs to support a VIEW mode and an ADMIN mode. The portlet will display the FAQ information as shown in Figures 3 and 4 in the VIEW mode. ADMIN mode will allow the manipulation of FAQ categories and questions. Note that the admin screens are not included in this article to keep it concise. The view mode supports the following render types:

  • ViewIndex: Portlet home page that displays a list of FAQ categories.
  • ViewCategory: Page displaying questions and answers belonging to the specified category.

The ADMIN mode needs to support the following render types:

  • AdminIndex: Home page for ADMIN mode. Provides links for creation, deletion, and editing categories.
  • CreateCategory: Displays a form for creating a new FAQ category.
  • EditCategory: Displays a form for editing an existing category.
  • EditQuestions: Main page for manipulating questions belonging to a category.
  • CreateQuestion: Displays a form for creating a FAQ question.
  • EditQuestion: Displays a form for editing a FAQ question.

The corresponding configuration in the portlet-config.xml file is shown in Listing 1. Notice that the JSP file used for generating portlet markup is specified by the include-path element. The directory path for the JSP file is specified by the base-dir element (not shown in the listing). In addition, there is a navigation node assigned to each render type, as specified in the nav element. Discussion of navigation implementation is out of scope for this article, but interested readers can look up the MVCPortlet User Guide for more details.

  <render-types>
    <render-type name="ViewIndex">
      <nav label="faq.view_index_header"
    reset="true" />
      <include-path name="default"
    path="ViewIndex.jsp" />
    </render-type>
    <render-type name="ViewCategory">
      <nav label="faq.view_category_header"
    idParam="cat_id" nameParam="cat_title" />
      <include-path path="ViewCategory.jsp" />
    </render-type>
    <render-type name="EditCategory"
  portletMode="admin">
      <nav label="faq.edit_category_header"
    idParam="cat_id" nameParam="cat_title" />
      <include-path path="EditCategory.jsp" />
    </render-type>
    <render-type name="CreateCategory"
  portletMode="admin" preCondition="admin">
      <nav label="faq.create_category_header" />
      <include-path path="CreateCategory.jsp" />
    </render-type>
    <render-type name="AdminIndex"
  portletMode="admin" preCondition="admin" >
      <nav label="faq.admin_index_header"
    reset="true" />
      <include-path name="default"
    path="AdminIndex.jsp" />
    </render-type>
    <render-type name="EditQuestions"
  portletMode="admin" preCondition="admin" >
      <nav label="faq.edit_questions_header" 
    idParam="cat_id" nameParam="cat_title" />
      <include-path path="EditQuestions.jsp" />
    </render-type>
    <render-type name="EditQuestion"
  portletMode="admin" preCondition="admin" >
      <nav label="faq.edit_question_header" 
    idParam="q_id" nameParam="q_title" />
      <include-path path="EditQuestion.jsp" />
    </render-type>
    <render-type name="CreateQuestion"
  portletMode="admin" preCondition="admin" >
      <nav label="faq.create_question_header" />
      <include-path path="CreateQuestion.jsp" />
    </render-type>
  </render-types>

Listing 1. Configuration of FAQ portlet render types

Defining Action Types

Category and question data used by the FAQ portlet is stored in a relational database, and implementing admin functionality requires a number of action types that create, delete, and modify database records. These action types are passed to processAction() method via the request parameter request_type. We need the following action types in the FAQ portlet:

  • CreateCategory: Create a new category record.
  • UpdateCategory: Update category information.
  • DeleteCategory: Delete a category record.
  • EditCategory: Used to populate the render parameters for the subsequent EditCategory render request.
  • CreateQuestion: Create a new question record.
  • UpdateQuestion: Update question information.
  • DeleteQuestion: Delete a question record.
  • EditQuestion: Used to populate the render parameters for the subsequent EditQuestion render request.

A fragment of the configuration for the action types listed above is shown in Listing 2. Let's look at the configuration of the action type CreateCategory to understand this process. The action processor for this type returns the key "success" if a new category record is successfully created. The configuration maps the key "success" to the render type AdminIndex. If the CreateCategory action succeeds, the render type will be set to AdminIndex, showing in the admin home page to the user. If there is a form validation error, the render type will be set to CreateCategory. This will result in the category creation form displayed along with the form validation error message. If the user cancels the operation, s/he will be taken back to the admin home page, as specified by the mapping for the key cancel.

  <action-types>

    <action-type name="CreateCategory"
  form="category" preCondition="admin" >
      <processor-result-mapping key="success"
    value="AdminIndex"
      message="faq.create_category_success_message" />
      <processor-result-mapping key="cancel"
    value="AdminIndex" />
      <processor-result-mapping key="validation-error"
    value="CreateCategory" />
    </action-type>

    <action-type name="DeleteCategory"
  preCondition="admin" >
      <processor-result-mapping key="success"
    value="AdminIndex" />
      <processor-result-mapping key="check-error"
    value="QuestionsExistError" />
    </action-type>
        :
        :
        :
  </action-types>
 

Listing 2. Configuration of FAQ portlet action types







Setting Up Access Control

MVCPortlet allows portlet deployers to specify access control for each render or action type. The configuration file has a separate section that defines named access control conditions. The permissions section for the FAQ portlet is shown in Listing 3. The configuration defines one precondition that allows access only for the user admin. For the FAQ portlet, we want to allow anyone to view non-admin functionality; i.e., render types corresponding to the VIEW mode. All other screens and actions should only be accessible to the user admin. This is achieved by specifying admin as the precondition name specified by the preCondition attribute of applicable render and action types.

  <pre-conditions>
    <pre-condition name="admin"
  class="com.nabhinc.rules.UserPrecondition">
      <users>admin</users>
    </pre-condition>
  </pre-conditions>
 

Listing 3. Permissions configuration for the FAQ portlet

Configuring RenderProcessors and ActionProcessors

The MVCPortlet framework provides a number of built-in processors for performing database-related operations. In many cases, including the above FAQ portlet, you can develop fully functional portlets without writing any Java code. As a result, implementing the business logic of a portlet is reduced to simply configuring existing processors. Configuration and mapping of ActionProcessors and RenderProcessors is very similar. A fragment of RenderProcessor configuration for the FAQ portlet is shown in Listing 4. The fragment shows the RenderProcessor for the render type ViewIndex. The render processor name is ViewIndex, which allows MVCPortlet to associate it with the render type with the same name. The configuration specifies the SelectRecords render processor that retrieves a vector of database records and sets the attribute mvcportlet.records to the record vector. The SQL for fetching the records is specified in <sql> element specified in the processor configuration.

  <render-processors>
    <render-processor name="ViewIndex"
      class="com.nabhinc.portlet.mvcportlet.renderprocessor.SelectRecords">
        <sql>SELECT catid, cattitle, catrank
  FROM SB_FAQ_CATEGORIES ORDER BY catrank</sql>
    </render-processor>

      :
      :

  </render-processors>
 

Listing 4. ViewIndex RenderProcessor configuration

Forms and Input Validation

The FAQ portlet requires user input when a category/question is being created or edited. Correspondingly, we have a form for accepting category information and a form for accepting question information. Listing 5 shows the forms configuration for the FAQ portlet. The configuration defines two forms: category and question. A form is associated with an action type by specifying its name in the form attribute of the <action-type> element, as shown in CreateCategory action type configuration shown in Listing 2.

  <forms>
    <form name="category">
      <field name="cat_id" />
      <field name="cat_title"
    validators="required,maxNameLength" />
      <field name="cat_rank"
    validators="int" default="0" />
    </form>
    <form name="question">
      <field name="cat_id" />
      <field name="q_id" />
      <field name="q_title"
    validators="required,maxNameLength" />
      <field name="q_answer"
    validators="required,maxDescrLength" />
      <field name="q_rank"
    validators="int" default="0" />
    </form>
  </forms>

Listing 5. Forms configuration for the FAQ portlet

The form named category defines three fields: category ID, category title, and category rank. Each field is optionally associated with one or more validators. For example, the field cat_title references two validators named required and maxNameLength, respectively. This is specified as a comma-delimited list in the validators attribute of the <field> element. The validators configuration section defines corresponding validators, as shown in Listing 6. Each validator has a name that must match the name specified in the form configuration. In addition, a validator can have additional attributes and sub-elements specific to that validator. For example, the maxNameLength validator uses the validator class com.nabhinc.portlet.mvcportlet.validator.MaxLengthValidator with a configuration attribute, maxLength, that provides the maximum length for the form field value.

  <validators>
    <validator name="required"
      class="com.nabhinc.portlet.mvcportlet.validator.RequiredValidator" />
    <validator name="int"
      class="com.nabhinc.portlet.mvcportlet.validator.IntegerValidator" />
    <validator name="maxNameLength"
      class="com.nabhinc.portlet.mvcportlet.validator.MaxLengthValidator"
      maxLength="100" />
    <validator name="maxDescrLength"
     class="com.nabhinc.portlet.mvcportlet.validator.MaxLengthValidator"
      maxLength="4000" />
  </validators>

Listing 6. Validators configuration for the FAQ portlet

Writing JSP Files

JSP files used to generate markup fragments can use all available aids, such as JSP tag libraries. The MVCPortlet framework provides a tag library and helper JSPs to facilitate this task. As an example, consider the JSP file shown in Listing 7 that displays the category creation form. The file first includes a helper JSP file, nav.jsp. This JSP file inserts various information messages set by Action/RenderProcessor objects invoked during the current requests. It also inserts navigation links at the top of the page. The file also uses a number of JSP tags provided by the MVCPortlet framework, including:

  • <mp:form>: This tag results in an HTML form tag with the form action set to the action URL for the current portlet. In addition, if the attribute validationMethod is specified, it generates client-side validation code for form fields.
  • <mp:formLabel>: This tag produces internationalized form field labels. If there was a validation error for the named form field, it also highlights those fields, depending on the portal theme styles.
  • <mp:input>: This tag generates a text input field and populates it based on the corresponding render parameter.
<%@include file="../include/nav.jsp" %>
<%
    String catTitle = rReq.getParameter("cat_title");
    if (catTitle == null) catTitle = "";
    String catRank = rReq.getParameter("cat_rank");
    if (catRank == null) catRank = "0";
    PortletURL actionURL = rRes.createActionURL();
%>
<table width="100%">
<mp:form name="category"
    validationMethod="validateCreateCategory">
  <input type="hidden" name="request_type"
      value="CreateCategory" />
<tr>
  <td class="portlet-font">
      <b><mp:formLabel key="faq.title_label"
  name="cat_title" /></b>
  </td>
  <td class="portlet-font">
      <mp:input name="cat_title" />
  </td>
</tr>
<tr>
  <td class="portlet-font">
      <b><mp:formLabel key="faq.rank_label"
  name="cat_rank" /></b>
  </td>
  <td class="portlet-font">
      <mp:input name="cat_rank" />
  </td>
</tr>
<tr>
  <td class="portlet-font" align="right" colspan="2">
    <br/>
    <mp:submit value="faq.create_category_button"/> 
    <mp:cancel value="mvcportlet.cancel_button" /> 

  </td>
</mp:form>
</table>

Listing 7. Example JSP

Portlet Deployment

At this point we have completed all portlet development tasks and the FAQ portlet is ready for deployment. Actual deployment steps will, of course, depend on the chosen portal server. We tested the FAQ portlet in Stringbeans, an open source portal server. To deploy the FAQ portlet in this container, a portlet entry (shown in Listing 8) is copied to the portlet.xml file located in WEB-INF directory of the portal web application. We then copied all JSP and configuration files to the portlets/faq directory under the web application root, and restarted the server. Finally, we added the portlet to an appropriate portal page to complete the portlet development and deployment process.

  <portlet>
    <description>Displays Site FAQ.</description>
    <portlet-name>FAQ</portlet-name>
    <portlet-class>
      com.nabhinc.portlet.mvcportlet.core.ControllerPortlet
    </portlet-class>
    <init-param>
      <name>configFile</name>
      <value>/portlets/faq/portlet-config.xml</value>
      </init-param>
      <supports>
        <mime-type>text/html</mime-type>
        <portlet-mode>ADMIN</portlet-mode>
      </supports>
      <supports>
        <mime-type>
          application/vnd.wap.xhtml+xml
        </mime-type>
      </supports>
      <portlet-info>
        <title>Frequently Asked Questions</title>
        <keywords>Collaboration</keywords>
      </portlet-info>
  </portlet>

Listing 8. FAQ portlet configuration in portlet.xml

Resources

width="1" height="1" border="0" alt=" " />
Padmanabh Dabke is the founder and CEO of Nabh Information Systems, Inc.