Licenses
MVCPortlet is a free software; you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
1. Introduction
MVCPortlet
TM is a framework for developing JSR 168 compliant portlets using a pattern similar to Model-View-Controller. MVCPortlet provides all essential elements needed for writing a well-behaved business portlet. The framework provides a "ControllerPortlet" that receives all "processAction" and "render" requests. The 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/EJ beans. The "view" is generated by forwarding the render requests to JSPs. The current features offered in MVCPortlet include:
- Development of JSR 168 compliant portlets based on MVC pattern
- 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
- Action specific access control
- Support fo file uploads
MVCPortlet is a project of Nabh Information Systems, Inc. The home page for the project is located at
http://www.nabh.com/projects/mvcportlet. The framework is distributed under GNU Public License (GPL).
2. A Crash Course on Portlets
A portlet is a server side component that lives in a "portlet container" provided by a "portal server" (e.g.
Stringbeans Portal Server). A portal server constructs a "portlet window" by adding decorations such as a title, navigational icons to the portlet fragment. Multiple portlet windows are assembled by a portal server to display a "portal page".
Portlet classes implement "javax.portlet.Portlet" interface defined by JSR 168 standard. The portlet lifecylce is expressed via four methods: "init", "render", "processAction", and "destroy". A portal server may create an instance of a portlet at the start up time or when the portlet receives the first request. It calls "init" method on the portlet before invoking any render or process action methods. The init method is called with a "PortletConfig" object that provides initial configuration parameters. The portlet should configure its state and retrieve resource needed for lifecycle operations in the init method.
When a portlet is placed in operation, it receives two types of requests "render" and "processAction". The render method is responsible for generating the markup fragment for display purposes. The render method is invoked with a "RenderRequest" object and a "RenderResponse" object. "RenderRequest" object is similar to "HttpServletRequest" object supplied in a servlet request. It provides request parameters that dictate the markup fragment generated by the portlet. RenderResponse object is similar to "HttpServletResponse" object supplied with a servlet request and must be used to output portlet markup.
The processAction method is usually invoked as a result of a form submission or clicking on a link configured to invoke a state change in a portlet. The processAction method is invoked with two arguments of types "ActionRequest" and "ActionResponse". A portlet executes implementation specific action in this method. After executing the action, it can choose to redirect the request. In this case, the portal server sends the redirect back to the Web client. Alternatively, a portlet can set some render parameters and return. In this case, the portal server invokes render method on all portlets on that page. The render parameters for all other portlets will be the same as previous render requests. The render parameters for the targeted portlets will be the ones set during processAction call.
The end of a portlet's lifecycle is indicated by invocation of "destroy" method. A portlet should clean up its resources in this method.
Please note this section provides a high level description of portlets. Hopefully this is enough to understand the rest of this document for the "impatient" ones who must read it without any background in portlet technology. Ideally, readers should have read at least JSR 168 portlet specification prior to reading this overview.
3. MVCPortlet Framework and 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), and the presentation (views). Decomposing the system in this fashion helps in maintaining system integrity while providing multiple way of viewing and editing the system.
3.1. 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 newer implementations create an object-oriented abstraction layer that represents the model.
Current release of MVCPortlet provides ActionProcessor and RenderProcessor implementations that perform database operations used in a typical data-driven application. In future we will add the ability to work with Java Beans and Enterprise Java Beans. 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.
3.2. View
The View portion of a MVCPortlet-based application is commonly constructed using Java Server Pages (JSP) technology. Technically, you can include output generated by plain HTML files or other servlets. JSP pages can leverage all the functionality available in standard JSP technologies, but one has to operate within the constraints and guidelines imposed by portlet API specification. For example, request parameters must not be obtained from "request" object available in a JSP page, but from RenderRequest object that is available either as a request attribute "javax.portlet.renderRequest" or by using
tag that defines a "renderRequest" system variable.
MVCPortlet provides a number of JSP tags to reduce the complexity of writing portlet JSP pages, and facilitate internationalization. Please refer to the tag library documentation for more information.
3.3. Controller
Controller portion of MVCPortlet (the ControllerPortlet) handles all incoming "processAction" and "render" requests. In both methods, the controller selects a processor based on a request parameter named "request_type". All processors used in render method implement "com.nabhinc.portlet.mvcportlet.core.RenderProcessor" interface. Processors used in processAction method implement "com.nabhinc.portlet.mvcportlet.ActionProcessor" interface.
Association between request type and a processor is defined in portlet-config.xml file typically located at the top level in the portlet base directory. In addition, users can specify a form attached to a action request type along with validators for validating user input. If configured properly, MVCPortlet can generate Javascript code for client side validation and also perform server side validation.
Processors return a logical name indicating the result of their execution. The configuration file maps the names to actual paths that are used to generate the display. This helps in separating the view generation from your processor classes.
4. ControllerPortlet Lifecycle/Control Flow
4.1. Initialization
ControllerPortlet initialization mainly consists of reading portlet configuration files and constructing instances of all supporting class needed for portlet operation. ControllerPortlet expects at least one parameter "configFile" in it's portlet configuration defined in "portlet.xml" file. The value of this parameter must be the context relative path for the portlet configuration file. In addition, you can also specifiy "messageFile". 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 portlet configuration file.
4.2. Render Request Processing
4.2.1. RenderConfig Lookup
When ControllerPotlet receives a render request, it first looks at the request type, i.e., the value of "request_type" parameter. If the request type is not specified it attempts to find the default request type for the current portlet mode. If it still does not find a request type, it throws a PortletException.
ControllerPortlet looks up the RenderConfig for the request type within the current portlet mode. Note that a RenderConfig object corresponds to a request type AND the portlet mode. The portlet mode is specified via "portletMode" attribute of "render-type" element in portlet configuration file. If ControllerPortlet cannot find a RenderConfig, it throws PortletException.
4.2.2. Permission Check
Deployers can assign associate a permission rule with a request type. This is done by specifying a precondition name in "preCondition" attribute of "render-type" configuration element. The permissioning pre-condition can involve user id check, role membership, etc. Please look at the Permissioning section of this document for more details. If the permissioning check fails, ControllerPortlet throws PortletSecurityException.
4.2.3. Navigation Path Manipulation
MVCPortlet allows associating a navigation node with a request type. Each navigation node specifies a label, id parameter, and a name parameter. If such a node is associated with the current render request, it is added to the navigation path. This path can be used in the display to show the user trail on the screen (e.g. Home -> View Category -> View Question). Please read Navigation Management section for more details on this topic.
4.2.4. Pre-Processing
If the render method passes through all the preceeding steps, ControllerPortlet invokes "preprocessRender" method. The default implementation of this method is empty. Developers can incorporate custom handling of render requests common to all request types by extending ControllerPortlet class and overriding this method.
4.2.5. Business Logic Execution
If there is a RenderProcessor associated with this request type, ControllerPortlet invokes "process" method on the RenderProcessor. The process method can choose to return a logical name that specifies the next step in business logic execution. The logical name can either map to an include path, or another RenderConfig. If it maps to an include path, we move on to View Generation step. If it maps to another RenderConfig, the same process is repeated for the next RenderConfig. Th
is feature can be used to chain multiple RenderProcessors within one request type processing.
4.2.6. View Generation
If there is no RenderProcessor associated with the request or if the last RenderProcessor returns null result string, ControllerPortlet looks up the default include path for the render type. A special key "default" is used to look up the default include path. If the last RenderProcessor returns a result string, it is used to look up the include path. MVCPortlet allows you to configure multiple include paths for the same key corresponding to XHTML/WML/HTML clients and window states. Once the actual include path is computed, request is forwarded to it via PortletRequestDispatcher to this path, thus completing processing of render request. For more information about the mapping between logical include names and the paths, please look at "Producing Client-Specific Output" section.
4.3. ProcessAction Request Processing
4.3.1. ActionConfig Lookup
ControllerPotlet first looks at the request type, i.e., the value of "request_type" parameter. If the request type is not specified it attempts to find the default request type for the current portlet mode. If it still does not find a request type, it throws a PortletException.
ControllerPortlet looks up the ActionConfig for the request type within the current portlet mode. Note that a ActionConfig object corresponds to a request type AND the portlet mode. The portlet mode is specified via "portletMode" attribute of "action-type" element in portlet configuration file. If ControllerPortlet cannot find a ActionConfig, it throws PortletException.
4.3.2. Permission Check
Deployers can assign associate a permission rule with a request type. This is done by specifying a precondition name in "preCondition" attribute of "action-type" configuration element. The permissioning pre-condition can involve user id check, role membership, etc. Please look at the Permissioning section of this document for more details. If the permissioning check fails, ControllerPortlet throws PortletSecurityException.
4.3.3. Pre-Processing
If the content type of the request is "multipart/form-data" and "wrapMultipart" attribute on the action type mapping is not set to false, the original request is wrapped in "MultipartRequestWrapper". The wrapper makes the form input fields available as first class request parameters. It makes FileItem objects corresponding to uploaded files via request attributes: mvcportlet.file.<file name>. ControllerPortlet then invokes "preprocessAction" method. The default implementation is empty. Customized portlet implementations should override this method . ControllerPortlet checks for the "cancel" request parameter. If the parameter value is not null, it assumes that the action request corresponds to form cancellation. In this event, it processes action configured for "cancel" processor result string.
4.3.4. Render Parameter Setup
ControllerPortlet has the ability to propogate parameters received in an actionProcess call to render parameters, or session attributes. By default, ControllerPortlet sets all parameters received via ActionRequest as render parameters. You can change this behavior via the "scope" attribute in "action-type" element. If the scope is specified as "portlet_session", Controller portlet sets corresponding session attributes in portlet scope. If it is specified as "application_session", Controller portlet sets corresponding session attributes under application scope (i.e., the attributes are visible in servlet session). If the scope is specified as "request", the effect is the same as the default behavior.
You can turn parameter transfer off by setting "setRenderParameters" attribute of "action-type" element to "false". This behavior is also turned off if you specify a "redirect" "processor-result-mapping" for this action. This is necessary since JSR 168 standard prohibits setting render parameters if processAction results in a request redirect.
4.3.5. Form Validation
If there is a form associated with this action type, form input fields are validated. If there is a validation error, ControllerPortlet processes action configured for "validation-error" result string. Please see "Input Validation" section for more information.
4.3.6. Business Logic Excecution
If processAction goes through all the preceeding steps succesfully, it invokes "process" method on the ActinoProcessor associated with the request type. This method returns a result string (e.g., "success"). The result string can map to three types of results. If it maps to a render type, Controller portlet sets the "request_type" render parameter to this value. It also sets status/info message params if configured and the processing stops. If it maps to a redirect, the same is returned as a redirect to the portlet container. For relative redirect URLs, Controller prepends the context path. If the result string maps to another processor, it invokes "process" method on that processor and the processing continues until a processor action results in a render type specification or a redirect.
4.4. Destruction
When the container calls "destroy" method on a ControllerPortlet instance, it calls "destroy" method of all registered ActionProcessor and RenderProcessor objects.
5. Input Validation
Validation of input data provided by the user is a critical step in any portal application. MVCPortlet provides the capability to do server and client (based on Javascript) validation. In many cases, developers will not have to write a single line of Java/Javascript code if they use the built-in features.
5.1. Server Side Validation
5.1.1. Configuration
The first step in setting up server side validation is to configure forms used in the applications. A typical form definition looks as follows:
<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>
Each "form" element defines a collection of named fields. A form field may be associated with zero or more validators as specified by "validators" attribute of the field element. The validators are referenced by their names. Validators are specified in a seperate section of the configuration file as shown below:
<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>
Each validator has a name used to identify the validator in the form. A "validator" element also has a "class" attribute which is used create an instance of that validator. It may have additional attributes and sub-elements. Please refer to JavaDoc documentation of validator classes for the details.
The last component in validation configuration is the association between action types and forms. This is done via the "form" attribute of "action-type" as shown in the following fragment:
<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>
5.1.2. Validation Process
In the above example, the action type "CreateCategory" is associated with a form named "category". In this example, form validation will be performed within "processAction" call on the servlet if the request type is "CreateCategory". Validation is performed on all form fields. For each field, Controller portlet invokes "validate" methods on each validator until there is a validation error, or all validations are successful.In case of validation errors, ControllerPortlet takes the following actions:
- It sets the render parameter "mvcportlet.form_error_message<portlet mode>" is to string "mvcportlet.form_error_message", where <portlet mode> is the portlet mode (e.g., edit, view, etc.).
- It sets the portlet session attribute "mvcportlet.form_errors" to a "FormErrors" object. This object allows looking up "FormError" objects representing all validation errors detected during validation. Each "FormError" object provides the name of the invalid form field and the validator name. The display JSPs can make use of this information in displaying the errors to the user.
- Finally, Controller portlet looks up the action corresponding to "validation-error" result string and executes it.
5.2. Client Side Validation
5.2.1. Javascript Configuration
Client side configuration is done via Javascript that provides validation equivalent to the server side validation performed by Java validator classes. This association between java classes and Javascript code to be executed on the client is typically specified in "validation-javascript.xml" file. ControllerPortlet first looks for a init parameter named "javascriptFile" If that is not specified, it looks for the file in portlet directory. If that does not exist, it loooks for the file in the include directory at the same level as the portlet base directory. The following fragment shows the structure of this file:
<javascript-mappings>
<javascript-mapping class="common">
<javascript><![CDATA[
// whitespace characters
:
:
]]>
</javascript>
</javascript-mapping>
<javascript-mapping class="com.nabhinc.portlet.mvcportlet.validator.RequiredValidator"
methodName="validateRequired">
<javascript><![CDATA[
function validateRequired (theField) {
:
:
]]>
</javascript>
</javascript-mapping>
</javascript-mapping>
As seen in the above XML fragment, the file specifies a mapping between a validator class and client side validation Javascript code. A special keyword "common" (which is not really a class) is used to indicate common Javascript functions that need to be included with any class.
5.2.2. Validation Process
At this time client side validation code can be generated only through the "form" tag provided by MVCPortlet tag library. A JSP fragment demonstrating use of this tag is shown below:
<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>
:
:
</mp:form>
The form tag must specifiy a form name (in this case "category") and a validation method name (in this case "validateCreateCategory". If these attributes are specified, the form tag will automatically create and write Javascript code using the following steps:
- It writes all Javascript corresponding to the class "common".
- It collects a set of all validators associated with the specified form, looks up javascript functions associated with each class and writes them to the output.
- The form validation function is dynamically generated such that it invokes one validation funtion corresponding to a validator function associated with the form. The function name is retrieved from the configuration. Function argument list is constructed by invoking "getJavascriptFunctionArgs" method on the validator.
The following Javascript fragment shows the validation function generated for our CreateCategory example:
var bCancel = false;
function validateCreateCategory(form) {
var elem;
var errs=0;
var errorMsg = "";
if (bCancel) {
return true;
} else {
if (!validateRequired(form.elements["cat_title"])) {
errs += 1;
errorMsg += "Category title cannot be blank.";
errorMsg += "\n";
}
else if (!validateMaxLength(form.elements["cat_title"], 100)) {
errs += 1;
errorMsg += "Category title cannot be more than 100 characters.";
errorMsg += "\n";
}
if (!validateInteger(form.elements["cat_rank"])) {
errs += 1;
errorMsg += "Category rank must be a number.";
errorMsg += "\n";
}
}
if (errs > 0) {
alert(errorMsg);
return false;
} else {
return true;
}
}
6. Permissioning
MVCPortlet provides the capability of associating permissioning rules with each request type. The preconditions used in permissioning are specified in a separate section. An example of such a configuration is given below:
<pre-conditions>
<pre-condition name="admin" class="com.nabhinc.rules.UserPrecondition">
<users>admin</users>
</pre-condition>
</pre-conditions>
This section can specify multiple preconditions. Each precondition has a name and a class as specified by attributes with the same name. It may also have additional attributes depending on the precondition class. MVCPortlet is distributed with precondition classes that allow permissioning based on user IDs, roles, database relations, client IP address and names, etc. These preconditions can be combined using composite precondition classes that provide AND/OR/NOT operations on constituent preconditions. For more details on the preconditions, please look up Javadoc for classes in com.nabhinc.rules package.
7. Internationalization
Internationalized message strings are specified in file portlet-messages.properties file. Messages defined in these files can be combined with tags provided in MVCPortlet tag library that accept a message key. At present MVCPortlet tags that support internationalization are: label, formLabel, cancel, For example, the JSP file for our CreateCategory example has the following tag:
<mp:formLabel key="faq.title_label" name="cat_title" />
In this case, portlet-messages.properties file can define the following properties:
faq.title_label=Title
faq.title_label.tr=baslik
In this case, the tag will look up the current locale associated with RenderRequest. If it is turkish, the title will be displayed as "baslik". For all other locales, you will see it displayed as "Title". If the locale also has a country associated with it, you can optionally add an uppercase abbreviation of the country code as shown below:
testportlet.advertize.en_US=Advertize
testportlet.advertize.en_UK=Advertise
Form errors display
As described earlier in the validation section, a "FormErrors" object is assigned to a session attribute named "mvcportlet.form_errors". This object contains a list of all error messages. These messages are displayed in "nav.jsp" file that is common to all portlet instances. If you use this file, you should define error strings of the form "mvcportlet.form_error.<form name>.<field name>.<validator name>. For example, if there is an error in "int" validator for the category rank field in our example, we can specify the corresponding error as follows:
mvcportlet.form_error.category.cat_rank.int=Category rank must be a number.
This error message will be displayed on the top of the form for server side validation. If you are also using client side validation, the error will be displayed as a Javascript alert.
8. Producing Client-Specific Markup
Applications that support multiple clients such as Web browsers, PDAs, mobile phones, etc. need to generate different client-specific markup fragments. MVCPortlet allows you to include different JSP files depending on the type of display requested. This is done in the "include-path" specification for within a "render-type" configuration. Controller portlet can identify the following types of clients:
- XHTML Mobile Profile clients - If the user agent accepts mime type "application/vnd.wap.xhtml+xml", the client is determined to be of type "xhtmlmp".
- WML clients - If the user agent accepts mime type "text/vnd.wap.wml", the client is determined to be type "wml".
- HTML clients - All other clients are treated as HTML clients. In this case the client type corresponds to the window state for the portlet: maximized, minimized, normal, etc.
You can specify different JSP files for different clients by specifying "displayType" attribute of the "include-path" element. For example, the following configuration specifies different files for all window states of HTML clients, WML clients, and XHTML MP clients:
<!-- Default include path -->
<include-path name="success" path="ViewIndex.jsp" />
<!-- Include path for WML capable mobile devices -->
<include-path name="success" path="ViewIndex_WML.jsp" />
<!-- Include path for XHTML-MP capable mobile devices -->
<include-path name="success" path="ViewIndex_XHTMLMP.jsp" />
9. Navigation Map
MVCPortlet allows you attach a navigation node to each render type. As users move between the screens, the navigation history is automatically maintained. Based on this history, you can display a navigation path on the top of your portlet. The navigation node association is defined as a sub-element of "render-type" element. As an example, consider the following configuration fragment:
<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>
The
ViewIndex render type is the main page. When users see this page, the localized version of
faq.view_index_header key will be displayed by the
nav tag provided by MVCPortlet. If a user clicks on a specific category, it will take them to the render type
ViewCategory with corresponding category "id" and "title". The navigation bar will now show something like:
FAQ Index -> View Category: <category name>. Attributes
idParam and
nameParam provide the value of render parameters used in constructing the view category link, if the users jump from View Category page to another page. Also notice that we have specified the
reset to be true for
ViewIndex node. This results in resetting the navigation node history when users come to the index page.
Copyrights and Acknowledgement
Copyright © 2005 Nabh Information System, Inc. All rights reserved.
- This product includes software developed by the Apache Software Foundation (http://www.apache.org/).
Copyright © 2005 Nabh Information System, Inc. All rights reserved.