Skip to main content

skip to main content

developerWorks  >  WebSphere  >

Developing a Spring Portlet MVC Framework application for use inside IBM WebSphere Portal: Advanced Spring Portlet MVC Framework

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss

Sample code


Rate this page

Help us improve this content


Level: Advanced

Sunil Patil (spatil@atech.com), Senior Consultant, Ascendant Technology LLC

20 Feb 2008

In the third and final article in our series, we cover more advanced topics in the Spring Portlet MVC Framework, including form validation, file upload, and handling exceptions. We also cover internationalization and integration of Apache Tiles Framework with Spring Portlet MVC Framework.

In the first part of this series, "Introduction to Spring Framework," we discussed how you can set up your development environment and how to create a simple HelloWorld application using Spring Portlet MVC Framework. In the second part, "Handling form submissions," we discussed how you can handle form submission in the Spring Portlet MVC Framework application and how to generate dynamic content based on the data submitted by a user. In this third and final part of the series, we cover some advanced topics related to Spring Portlet MVC Framework.

This article discusses how you can handle form submission, file upload, internationalization, and exceptions. Before finishing this article, we cover how you can integrate Apache Tiles Framework with Spring Portlet MVC Framework.

Form validation

Most real-world applications validate the data entered by the user on the form before executing an action. In this section, we change the Contact Management Portlet you developed in part two of the series to make sure that ContactID, FirstName, and LastName are required fields before inserting a new Contact. Note that we discuss how to perform validation on the server side instead of the client side.


Listing 1. Code listing ContactValidator.java
public class ContactValidator implements Validator {
	public boolean supports(Class givenClass) {
		return givenClass.equals(Contact.class);
	}
	public void validate(Object object, Errors errors) {
		Contact contact = (Contact) object;
		validateContactId(contact, errors);
		validateFirstName(contact, errors);
		validateLastName(contact, errors);
		validateEmailName(contact,errors);
		validatePhoneNumber(contact,errors);
	}
	public void validateContactId(Contact contact, Errors errors) {
		ValidationUtils.rejectIfEmpty(errors, "contactId",
				"required");
	}
	// Other validation related methods
}

Share this...

digg Digg this story
del.icio.us Post to del.icio.us
Slashdot Slashdot it!

Spring Framework defines the Validator interface that you implement when developing your validation class:

  • supports(Class)
    This method returns a Boolean value that indicates whether or not this Validator class is used for validating the supplied object. In our sample code, ContactValidator can validate only the Contact class, so we check if the supplied class is Contact. If it is, then return true; if it is not, then return false.
  • validate(Object,Errors)
    This method implements the actual validation logic on the supplied object. It validates the supplied object and, in case of validation errors, it registers those with the given Errors Object. In our sample case, we check if ContactId is empty. If it is empty, then we add the validation error that ContactId is a required field. When you do that, Spring Portlet MVC Framework tries to find the required.contact.contactId key in the ResourceBundle and use its value to display the error to the user.

We talk more about ResourceBundle later in the section on internationalization. For now, create the messages.properties file in the /src/main/webapp/WEB-INF/classes folder and add the validation messages as shown in listing 2.


Listing 2. Code listing for messages.properties
required.contact.firstName=First name is required field
required.contact.contactId=Contact Id is required field
required.contact.lastName=Last Name is required field

The next step in adding validation is to decide how you want to display the validation error messages to the user. In our sample code, if the user does not enter the value of ContactId, then we want to display a red message, "ContactId is required field," next to the ContactId input. To do that, we need to change the insert.jsp file as shown in listing 3.


Listing 3. Code listing for changes in insert.jsp
<tr>
	<td>Contact Id</td>
	<td><form:input path="contactId" size="30" maxlength="80"/></td>
	<td><form:errors path="contactId" cssStyle="color:red" /></td>
</tr>

The <form:errors> element is used to display the validation error message. It generates the <span> HTML element in the final markup. In the sample code, we want to display the validation error message related to contactId, so the value of the path attribute should be contactId. Because we want to display this error message in red, the color value of cssStyle should be color:red. If you don't specify path="contactId", it displays all the validation errors on the current form.

The last step is to make Spring Portlet MVC Framework aware that we intend to use ContactValidator as the validator class for InsertController. For this, we make the following changes in SpringContactManagPortlet-portlet.xml as shown in listing 4.


Listing 4. Code listing for changes in SpringContactManagPortlet-portlet.xml
<bean id="contactValidator" 
class="com.ibm.developerworks.contact.validation.ContactValidator"/>

<bean id="insertController"
class="com.ibm.developerworks.contact.controller.InsertController">
		<property name="contactDAO" ref="contactDAO"></property>
		<property name="commandName" value="contact"/>
<property name="commandClass" value="com.ibm.developerworks.contact.domain.Contact" />
		<property name="formView" value="insert"></property>
		<property name="successView" value="list"></property>
		<property name="validator" ref="contactValidator" />
</bean>

First, define the contactValidator bean; then, add it as the value of the validator property in the insertController bean definition. Now build your code, and try your changes on IBM WebSphere Portal to make sure that the validation logic works.

Handling file upload

Spring Portlet MVC Framework has built-in support for handling file upload in the portlet application. In this section, we change the Contact Management application to demonstrate how to handle file upload in Spring Portlet MVC Framework. First, we add Upload Contact File as a link on the Contact Update page. When the user clicks that link, we show the user a form with File Input. When the user selects a file and clicks Submit, we print the contents of the file in the System.out stream.

NOTE: Spring Portlet MVC Framework depends upon Apache Commons File Upload for the actual reading of the file from the input stream. Support for handling the JSR-168 portlet request was introduced in Apache Commons File Upload version 1.1, so make sure that you use version 1.1 or later. The build script for our sample code downloads version 1.1 of the Commons file upload JAR file.

The ability to handle a multipart request (File Upload) is disabled in Spring Portlet MVC Framework by default, so you need to enable that support by defining the portletMultipartResolver bean in your portlet application context file. See the code in listing 5.


Listing 5. Code listing for changes in SpringContactManagPortlet-portlet.xml
<bean id="portletMultipartResolver"
class= "org.springframework.web.portlet.multipart.CommonsPortletMultipartResolver">
	<property name="maxUploadSize" value="100000" />
</bean>

<bean id="contactFileUploadController" 
class= "com.ibm.developerworks.contact.controller.ContactFileUploadController">
	<property name="commandClass"
	value="com.ibm.developerworks.contact.domain.ContactFile" />
	<property name="formView" value="file"></property>
	<property name="successView" value="update"></property>
</bean>

After you add the definition of portletMultipartResolver in the Spring application context file, DispatcherPortlet examines every action request that it receives to check if it is multipart. If it is, then it wraps the incoming ActionRequest into MultipartActionRequest to handle the file upload. The contactFileUploadController bean definition tells Spring Portlet MVC Framework that ContactFileUploadController should be used for handling file upload.

Change the definition of the portletModeParameterHandlerMapping bean to map the contactFileUpload action to the contactFileUploadController bean. Whenever Spring Framework receives a request with an action equal to contactFileUpload, it forwards it to ContactFileUploadController.java for handling.

Now create the ContactFile.java class, which is commandClass as shown in listing 6.


Listing 6. Code listing for ContactFile.java
public class ContactFile {
	private byte[] contactFile;
	public byte[] getContactFile() {
		return contactFile;
	}
	public void setContactFile(byte[] contactFile) {
		this.contactFile = contactFile;
	}
}

This very simple Java bean has only one byte[] property that is used for storing the file uploaded by the user. After that, create ContactFileUploadController.java as shown in listing 7.


Listing 7. Code listing for ContactFileUploadController.java
public class ContactFileUploadController extends SimpleFormController{

protected void onSubmitAction(ActionRequest request, ActionResponse response, 
Object command, BindException errors) throws Exception {
		ContactFile contactFile =(ContactFile)command;
		if(contactFile != null){
			System.out.write(contactFile.getContactFile());
log.debug(" Contact File " + contactFile.getContactFile());
		}
		response.setRenderParameter("action", "list");
	}	
protected void initBinder(PortletRequest request, 
PortletRequestDataBinder binder)throws Exception {
binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
	}

}

Because the ContactFileUploader class is used for handling form submission, it extends the SimpleFormController class and overrides two methods:

  • initBinder()
    In the sample code, we want PortletRequestDataBinder to read the file uploaded by the user and to convert it into byte[]. We register ByteArrayMultipartFileEditor as the custom editor used for converting the multipart file into byte[].
  • onSubmitAction()
    In our sample code, we want to read the file uploaded by the user, and then redirect the user back to the Contact Spreadsheet so we override the onSubmitAction() method that gets called when the form is submitted. In this method, we write the content of the file into System.out, and then we set the value of the action render parameter to list, which allows us to pass control to SelectController for the actual rendering.

Now create file.jsp as shown in listing 8.


Listing 8. Code listing for file.jsp
<form method="post" action='<portlet:actionURL>
	<portlet:param name="action" value="contactFileUpload"/>
</portlet:actionURL>' enctype="multipart/form-data">
	<table cellpadding="4">
		<tr>
			<td>File</td>
			<td><input type="file" name="contactFile"/></td>
		</tr>
		<tr>
<td><input type="submit" name="_finish" value="Save"/></td>
<td><input type="submit" name="_cancel" value="Cancel"/></td>
		</tr>		
	</table>
</form>	

As you can see, file.jsp is a normal HTML form with one difference: The value of enctype is multipart/form-data, which tells the browser how to encode the multipart fields. Now compile your changes and try uploading any TXT file on the server. You should be able to see that the content of that TXT file is written in the SystemOut.log file.



Back to top


Enabling logging for your portlet

Generating sufficient and useful information in the log helps you after you deploy your application. Spring Framework uses the Jakarta Commons logging interface for logging. You can enable the Spring Framework log by adding a trace string.

NOTE: WebSphere Portal provides its own implementation of org.apache.commons.logging.LogFactory and org.apache.commons.logging.Log, which redirect your log messages to the WebSphere Portal trace file. You can enable or disable this log by making changes in Portal administration - Portal analysis - Enable tracing. Add the entries to enable the desired logging of the packages:

org.springframework.*=all

Then, check the trace.log file in the <portalserver>/log folder. Enabling trace for org.springframework could generate unnecessary information, so in the case of Spring Portlet MVC Framework, enabling trace only for org.springframework.web.*=all or org.springframework.web.portlet.*=all should be sufficient. You may have noticed that we use Commons logging in the sample code for this article. You can enable trace information for your application by enabling trace for com.ibm.developerworks.springmvc.*=all.



Back to top


Adding support for internationalization

Supporting more than one locale is a common requirement for most Web applications today, and portlets are no exception. Spring Portlet MVC Framework provides an internationalization infrastructure for your portlet that allows you to concentrate on the application's business logic instead of on how to load different resource files depending upon the locale of the user request.

Displaying locale-specific messages

Let's change the Contact Management Portlet to display a locale-specific version of the "Add New Contact" label as the title on the insert.jsp file. To keep things simple, we support only English, French, and German locales. Use the code shown in listing 9.


Listing 9. Code listing for messageSource bean
<bean id="messageSource" 
class= "org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
		<list>
			<value>messages</value>
		</list>
	</property>										
</bean>

The ResourceBundleMessageSource bean is used to resolve a message for the requested locale. It takes the fully qualified name of the properties file containing the messages as the value of the basename property. In our sample code, we want to use only one messages.properties file so we pass only one value in the basenames list. If you have more than one resource, then you can add the resources in the list of values. Note that ResourceBundleMessageSource is based on the JDK's ResourceBundle implementation, so it tries to find these resource files in either the /lib or the classes directory of a Web application's WAR structure. The ResourceBundleMessageSource class caches both the accessed ResourceBundle instance and the generated MessageFormat for each message for faster execution. Go to insert.jsp, and change the title as shown here:

<h3><spring:message code="insert.localeSpecificMessage"/></h3>

Inside insert.jsp, we use the Spring tag library's message tag to display a locale-specific message instead of hard-coding a message in the JSP file. Your next step is to create the message.properties file under the /WEB-INF/classes folder:

insert.localeSpecificMessage=Add New Contact - English Version

This file contains only the insert.localeSpecificMessage key, which is used in View.jsp. Create messages_fr.properties and messages_de.properties in the /WEB-INF/classes folder. Change the value of the insert.localeSpecificMessage key to indicate the locale. Alternatively, you can copy these two files from the sample code for this article.

Now build your code, and deploy it on the server. Change the language for your browser to either German or French and try to access the Insert new Contact Page on the Contact Management Portlet. You should see a greeting message specific to your locale.

Displaying locale-specific JSP pages

The approach of using the <spring:message> tag works if your JSP file has only a few messages. If you are creating help pages, though, it may be a good idea to display a different JSP to the user based on his locale. Follow these steps to change the Contact Management Portlet so that it displays different Help pages to the user based on his locale.

  1. Create Help_fr.jsp and Help_de.jsp files in the /WEB-INF/jsp folder. Your Help_fr.jsp file should look like the code shown in listing 10.

    Listing 10. Code listing for Help.jsp
                            
    <%@page language="java" contentType="text/html; charset=ISO-8859-1"
    	pageEncoding="ISO-8859-1" session="false"%>
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
    <portlet:defineObjects />
    <p>Hello from Spring Portlet MVC Framework - Help Mode - French Version</p>
    	

  2. Create the help.properties file in the /WEB-INF/classes folder:

    Help.class = org.springframework.web.servlet.view.JstlView
    Help.url=/WEB-INF/jsp/Help.jsp

  3. Similarly, create the Help_de.properties file and the Help_fr.properties file in the /WEB-INF/classes folder.

Try your changes on the server. You should see different Help pages based on the user's locale.



Back to top


Exception handling

The ability to handle exceptions gracefully is one of the key requirements for a good user interface. Imagine that you are transferring money using an online banking application, and the system shows you a database exception error. Would you want to use that site again?

In this section, we change the contact management application to demonstrate the use of Spring Portlet MVC Framework's exception-handling capability. We change the InsertController.java file so that when the user tries to add a new contact, we check if a contact with that contact ID already exists in the database. If it does, then we redirect the user to an error page.

First, define DuplicateContactIdException as shown in listing 12.


Listing 12. Code listing for help.properties
	public class DuplicateContactIdException extends Exception{
	public DuplicateContactIdException(String message){
		super(message);
	}
}

The DuplicateContactIdException class represents a checked exception. Whenever the user tries to add a duplicate contact ID, we throw this exception. Next, change the onSubmitAction() method in InsertController.java as shown in listing 13.


Listing 13. Code listing for changes in InsertController.java
protected void onSubmitAction(ActionRequest request, ActionResponse response, 
Object command, BindException errors) throws Exception {
		Contact contact =(Contact)command;
		if(contactDAO.getContact(contact.getContactId()) != null)
throw new DuplicateContactIdException("Contact with Contact Id " + 
contact.getContactId() +" already exists");
		contactDAO.insertContact((Contact)contact);
		response.setRenderParameter("action", "list");
}

As you can see, when the user tries to add a new contact, the onSubmitAction() method of InsertController class is called. Inside this method, we check if the contact with the supplied contact ID already exists. If it does, we throw the DuplicateContactIdException. If it does not, we insert the contact. After that, we create the error.jsp file as shown in listing 14.


Listing 14. Code listing for error.jsp
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<h3>An Error Occurred</h3>
message: <c:out value="${exception.message}"/>
<p style="text-align:center;"><a href="<portlet:renderURL portletMode="view" 
windowState="normal"/>">- Back to home page -</a></p>

The error.jsp file is quite simple. It informs the user that an error occurred, and then it retrieves the actual exception message and displays it to the user. After that, it generates a link that takes the user back to the home page, which is Contact Spreadsheet in this case.


Listing 15. Code listing for changes
<bean id="defaultExceptionHandlerTemplate"
	class="org.springframework.web.portlet.handler.SimpleMappingExceptionResolver" 
	abstract="true">
		<property name="defaultErrorView" value="error" />
		<property name="exceptionMappings">
			<props>
				<prop key= "com.ibm.developerworks.contact.exception.DuplicateContactIdException">
					error
				</prop>
				<prop key="java.lang.NullPointerException">
					error
				</prop>
			</props>
		</property>
	</bean>

	<bean id="defaultExceptionHandler" parent="defaultExceptionHandlerTemplate" />

Whenever DispatcherPortlet encounters an exception during request processing, it tries to iterate through all the handlerExceptionResolver beans defined in your portlet application context file. After it finds a matching ExceptionResolver, it calls its resolveException() method.

The SimpleMappingExceptionResolver is a simple implementation of HandlerExceptionResolver that allows the mapping exception class name to view names. In our sample code, we say that whenever there is either DuplicateContactIdException or NullPointerException, forward the control to the error.jsp file for the actual markup generation.



Back to top


Integration with Apache Tiles Framework

Using a templating framework like Apache Tiles Framework makes your UI development easy. For example, you can create one page with copyright information about your application and add it to all your pages instead of copying the copyright section manually to every page. In this section, we change the HelloSpringPortletMVC portlet developed in the first part of this series to demonstrate the integration with Apache Tiles Framework. We create Header.jsp and Footer.jsp files that are used to display the Header and Footer on every page. First, create the tiles-def.xml file in the /WEB-INF/defs folder as shown in listing 16.


Listing 16. Code listing for tiles-def.xml
<tiles-definitions>
   <definition name="View" page="/WEB-INF/jsp/tiles/TilesLayout.jsp" >
      <put name="banner" value="/WEB-INF/jsp/tiles/Header.jsp" />
      <put name="body" value="/WEB-INF/jsp/View.jsp" />
      <put name="footer" value="/WEB-INF/jsp/tiles/Footer.jsp" />
   </definition>
</tiles-definitions>

The tiles-def.xml file is an Apache Tiles definition file. In this file, View is the first tiles definition. The View tiles definition says that the TilesLayout.jsp page is the template page for this definition. It takes three properties:

  • A banner pointing to Header.jsp
  • The body pointing to View.jsp
  • A footer pointing to Footer.jsp

NOTE: If you're new to Apache Tiles and want to know more, visit the Apache Software Foundation Web site for a Tiles tutorial.

Next, create the TilesLayout.jsp page in the /WEB-INF/jsp/tiles folder; see listing 17.


Listing 17. Code listing for TilesLayout.jsp
<%@ taglib uri="/tags/struts-tiles" prefix="tiles" %>
<table>
	<tr>
		<!-- Insert content of banner.jsp -->
		<td><tiles:insert attribute="banner"/></td>
	</tr>	
	<tr>
		<!-- Depending on action actual content will be inserted here -->
		<td><tiles:insert attribute="body"/></td>
	</tr>	
	<tr>
		<!-- Insert content of footer.jsp  -->
		<td><tiles:insert attribute="footer"/></td>
	</tr>	
</table>

The TilesLayout.jsp page is the tiles layout page. It defines a simple table with three rows.

  • The first row inserts the content of the banner page, which is Header.jsp in our case.
  • The second row displays the content of the body page, which is View.jsp, Edit.jsp, or Help.jsp in our sample code.
  • The last row displays the content of the Footer.jsp file.

After that, you want to let Spring Portlet MVC Framework know about this tile's definition file. For that, we have to modify the HelloSpringPortletMVC-portlet.xml file as shown in listing 18.


Listing 18. Code listing for changes in HelloSpringPortletMVC-portlet.xml file
<bean id="tilesConfigurer" 
class= "org.springframework.web.servlet.view.tiles.TilesConfigurer">
		<property name="definitions">
			<list>
				<value>/WEB-INF/defs/tiles-def.xml</value>
			</list>
		</property>
</bean>

As you can see, we added the definition of the tilesConfigurer bean in the HelloSpringPortletMVC-portlet.xml. The tilesConfigurer bean simply configures a Tiles Definition factory using a set of files containing the tiles definition. In our sample code, we have only the tiles definition file so the value of the definitions property takes a list with its only value pointing to the tiles-def.xml file.

Next, make a couple of changes in the applicationContext.xml file. We want the HelloSpringPortletMVC portlet to use the Tiles view instead of the JSP view. First, delete or comment out the definition of InternalResourceViewResolver, and then add the new definition for TilesView resolver as shown in listing 19.


Listing 19. Code listing for changes in applicationContext.xml file
<bean id="viewResolver" 
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<property name="requestContextAttribute" 
value="requestContext" />
	<property name="viewClass"
	value="org.springframework.web.servlet.view.tiles.TilesView" />
</bean>

In the case of Spring Portlet MVC Framework, InternalResourceViewResolver is used to resolve both the JSP page view and the TilesView. In our sample code, the viewResolver bean has a property called viewClass with a value equal to org.springframework.web.servlet.view.tiles.TilesView. This tells Spring Portlet MVC Framework that we want to display TilesView instead of the normal JstlView.

Your last step is to create the Header.jsp and Footer.jsp files in your /WEB-INF/jsp/tiles folder. Alternatively, you can simply copy them from the sample code in the Download section of this article. Note that we use the Tiles taglib in the TilesLayout.jsp file, so you need to copy the tld folder from the sample code for this article. Also, do not forgot to copy the <taglib> definitions in the web.xml file from the sample code for this application. After you have done this, build your code by executing the mvn package command, and test your changes on server.



Back to top


Conclusion

In this article, we covered some of the advanced topics in Spring Portlet MVC Framework. We started by discussing server-side form validation; then we covered how you can handle file upload. After that, we discussed support for internationalization in Spring Portlet MVC Framework and how to handle exceptions gracefully. We also covered integration of Apache Tiles Framework with Spring Portlet MVC Framework.

One of Spring Portlet MVC Framework's biggest advantages is that because it is Spring Module, it provides easy integration with Spring Framework, which is a full-stack J2EE application framework. It also provides the infrastructure code for common J2EE requirements, and it works easily with popular open source frameworks such as Hibernate and iBatis.




Back to top


Download

DescriptionNameSizeDownload method
Article sample portletpart-3.zip50 KBHTTP
Information about download methods


Resources



About the author

Sunil Patil is a Senior Consultant working at Ascendant Technology, LLC. He is the author of several IBM developerWorks articles and a book, Java Portlets 101.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top