Using Spring MVC with Nitobi CUI

3 Comments

I've been using Nitobi for a while now, and most of it has been in writing the JSF components or consulting for Struts 2 or plain Servlets. However, as the industry (and my own work) has been using Spring more and more, I thought a nice tutorial for integrating Spring MVC with the Nitobi Taglib and server libraries would be helpful. The source code for this tutorial is here (NOTE: the source code is not uploading to the blog... working on it. If you want it, leave your email)

The CUI

With the open-sourcing of the client-side of the components, more and more people are seeing how nifty the components are. There are a lot of other OS javascript frameworks, but few are as detailed and powerful as the Nitobi ones. For example, the tabs components are matched in basic functionality by a hundred other frameworks, but most others don't have a declarative style and very few have as many event hooks. The same can be said of the Grid, which is pretty much the flagship component of the CUI.

Compressed XML

From a birds-eye view, the Java server side simply takes an HTTP request and produces a "compressed" XML document to relay information back to the client. At first glance, the compressed markup seems pretty strange, but taking into consideration a component that sends thousands of records back and forth it begins to makes sense. For example, if you have 8 attributes in a single XML record and each of the attribute names are an average of 6 characters long, you have 48 bytes per record just in the attribute names alone. The compressed format reduces each attribute name down to a letter that corresponds to a key at the top of the XML. Thus, a record with 8 attributes would only waste 8 bytes on the attribute names. With a grid containing a thousand records (with or without paging), you would save 40K in attribute names alone! Snippet 1 is an example of a human-readable version of the compressed XML.

Snippet 1 - compressedXML.xml

<?xml version="1.0" encoding="UTF-8" ?>
<root xml:lang="en" fields="address|contactEmail|contactName|contactTitle|country|customerName|phoneNumber">
<e xk="84" a="2 East 121st Avenue, Rockford, Mi, 92467" b="deandrealvarez@picrific.com" c="Deandre Alvarez" d="Media Director" e="USA" f="247Smith Ltd." g="(262) 242-3365" />
<e xk="23" a="91 East Henson Drive, Brodhead, Ky, 74256" b="mgran@toronto.com" c="Macie Grant" d="Media Buyer" e="USA" f="Air Cast" g="(224) 486-6464" />
<e xk="1" a="411 Hideaway Lake Drive, Buffalo, Wy, 35154" b="fdacker@ajc.com" c="Fran Dacker" d="Office Manager" e="USA" f="Ajax Contractors Ltd." g="(427) 385-2762" />
<e xk="92" a="36 Grissom Circle, Mooringsport, La, 72681" b="fsteven@dasani.org" c="Faith Stevens" d="CEO" e="USA" f="Allstate Robot" g="(573) 676-3256" />
</root>

Java Server Libraries

The Java server libraries, although they cost money, quickly pay for themselves in the time saved. The libraries make converting a list of beans simple, and the Taglibs make managing the declarative syntax simpler and automate a lot of the monotony.

Basic Setup

The setup is no different than any other Spring MVC web application. The two additional libraries needed are the nitobi-server-lib.jar and nitobi-cui-taglib.jar archives. This tutorial has two Spring configuration files, applicationContext.xml and dispatcher-servlet.xml. Both of these are placed into the WEB-INF directory and registered through the web.xml. Snippet 2 shows the web.xml file.

Snippet 2 - web.xml


<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.xml</url-pattern> </servlet-mapping></web-app>

Notice that there isn't anything special in the web.xml for Nitobi. For the purposes of this demo, I chose to map the data as *.xml, but of course in a real app, you would probably have a better setup that factored non-data pages and forms.

The data for this tutorial is obtained from the samples sql that comes with the Nitobi distro. To see how Spring could be used throughout the app, I've set my DAO up extending Spring's JdbcDaoSupport class using a RowMapper to map the results to the Customer.java model bean. This way, Spring will populate the list for me. Snippet 3 shows the applicationContext.xml file which is where the DAO layer is configured.

Snippet 3 - applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="nitobiDatasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" scope="singleton">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/nitobi_testdb_v1"/>
<property name="username" value="nitobi"/>
</bean>

<bean id="customerRowMapper" class="org.springframework.jdbc.core.BeanPropertyRowMapper">
<property name="mappedClass" value="com.nitobi.samples.spring.model.Customer"/>
</bean>

<bean id="nitobiJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="nitobiDatasource"/>
<property name="resultsMapCaseInsensitive" value="false"/>
</bean>

<bean id="customerDAO" class="com.nitobi.samples.spring.db.CustomerDAO">
<constructor-arg ref="customerRowMapper"/>
<property name="jdbcTemplate" ref="nitobiJdbcTemplate"/>
<property name="dataSource" ref="nitobiDatasource"/>
</bean>
</beans>

The Model

The only thing displayed on the resulting page is a list of customers, so the model is pretty simple. The DAO gives back a list of Customer, each with an id field. Having an id field in the model object has special meaning for the GetHandler when the Controller class passes the List of Customers to the populate() method. If a model object has an id field, you can just pass the list to the GetHandler, which will use the id as the primary key (the 'xk' field in the compressed XML). Although the Customer class is just a simple POJO, I'm including it in Snippet 4 so you can see how the bean converts to the compressed XML in Snippet 1.

Snippet 4 - Customer.java


package com.nitobi.samples.spring.model;

/**
* A class representing a single customer.
*
*
@author <a href="mailto:eric.buitenhuis@giglinesoftware.com">Eric Buitenhuis</a>
*/

@SuppressWarnings({
"ALL"}) // keep the style checker from complaining about not having an interface
public class Customer {

private String id;
private String customerName;
private String contactName;
private String address;
private String contactEmail;
private String contactTitle;
private String country;
private String phoneNumber;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getContactEmail() {
return contactEmail;
}

public void setContactEmail(String contactEmail) {
this.contactEmail = contactEmail;
}

public String getContactName() {
return contactName;
}

public void setContactName(String contactName) {
this.contactName = contactName;
}

public String getContactTitle() {
return contactTitle;
}

public void setContactTitle(String contactTitle) {
this.contactTitle = contactTitle;
}

public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public String getCustomerName() {
return customerName;
}

public void setCustomerName(String customerName) {
this.customerName = customerName;
}

public String getPhoneNumber() {
return phoneNumber;
}

public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}

Take note of the names and cases for compressed XML fields. The GetHandler will alphabetize all the fields and include them in the fields="address|contactEmail|contactName|contactTitle|country|customerName|phoneNumber" attribute in the root node of the compressed XML. The JavaScript components will then take the fields as an array of fields, where address has the index of 0, contactEmail has the index of 1, etc. More on this when we get to the JSP <ntb:combo .../> portion.

The Controller

The CustomerComboController.java file extends Spring's Controller class and simply handles the request coming in from the ComboBox component. Although there are more creative ways to set Spring up (perhaps injecting pre-configured GetHandlers into the bean), I kept it simple. Spring will inject the CustomerDAO into the CustomerComboController, so you don't have to worry about instantiating and setup.

The first thing to do in the method is to grab the GET parameters from the HTTPServletRequest object. Each Nitobi component sends a different set of default GET parameters, so take a look at the reference materials at Nitobi Support to get the details. Since our demo only uses the ComboBox, we need to grab the params shown in Table 1.

Table 1
ComboId The DOM id given to the combo when declared in the JSP
LastString This is a field used for other systems that don't have proper paging. It generally isn't used with Java based systems
SearchSubstring This is the partial string used for type forwarding. Basically, this contains the content entered into the ComboBox thus far
PageSize The amount of records you want returned at a time when paging.
StartingRecordIndex The beginning index of the page to be returned.
nitobi_cachebust A mechanism for proper cache usage in IE. For all intents and purposes we can ignore this.

For this tutorial, we will only pay attention to the SearchSubstring, the PageSize, and the StartingRecordIndex. These values we pass back to the DAO so we can get the appropriate results. Note that this method does make the app pretty database intensive, and you might be better off using a caching mechanism. This is especially true for data that rarely changes.

The next step for the controller is to simply call the populate() method on the GetHandler object, passing it the list of Customer objects and the Customer.class object it will use to determine what kind of objects are in the List. Snippet 5 has the entire CustomerComboController class.

Snippet 5 - CustomerComboController.java

package com.nitobi.samples.spring.web;

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.nitobi.samples.spring.db.CustomerDAO;
import com.nitobi.samples.spring.model.Customer;
import com.nitobi.server.handler.GetHandler;

import java.util.logging.Logger;

/**
* The SpringMVC controller class that handles the Customer ComboBox.
*
*
@author <a href="mailto:eric.buitenhuis@giglinesoftware.com">Eric Buitenhuis</a>
*/

public class CustomerComboController implements Controller {

private CustomerDAO customerDAO;

private static Logger logger = Logger.getLogger(CustomerComboController.class.getName());

/**
* This method is ultimately called by the client and simply grabs the parameters and
* loads the Customer objects from a call the DAO.
*
*
@param httpServletRequest The request from the client
*
@param httpServletResponse The response to the client
*
@return Return null in this case, since the Nitobi GetHandler will perform the response.
*
@throws Exception When something really bad happens
*/

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {

/*
* The Nitobi GetHandler class. This is obtained from the server library included
* with the Java package at Nitobi.
*/

GetHandler getHandler =
new GetHandler();

/*
* Grab the GET parameters from the request object. Here you could also
* hook in validation.
*/

Object partialName = httpServletRequest.getParameter(
"SearchSubstring");
Object beginIndex = httpServletRequest.getParameter(
"StartingRecordIndex");
Object endIndex = httpServletRequest.getParameter(
"PageSize");

logger.info(
"Handling customers request - "
+
"SearchSubstring: " + partialName
+
" StartRecordIndex: " + beginIndex
+
" Page Size: " + endIndex);

/*
* The customerDAO object should not be null unless the dependency injection failed.
*/

if(customerDAO != null) {

/*
* Pass the list of objects to the getHandler, which does the work to convert the beans
* to the compressed XML.
*/

getHandler.populate(
customerDAO.loadCustomerByPartialName(partialName, beginIndex, endIndex),
Customer.
class
);

}
else {
/*
* Setting the error message here will include an error field in the XML. Use the onHandleError event
* in the ComboBox markup to grab this message when it exists.
*/

getHandler.setErrorMessage(
"The server could not access the data necessary to populate the Customer ComboBox.");
}

/*
* Write the getHandler content to the response stream. This is the line that actually sends the XML
* back to the client.
*/

getHandler.writeToClient(httpServletResponse);

/*
* Return null here, since we don't want to pass back a ModelAndView object
*/

return null;
}

public CustomerDAO getCustomerDAO() {
return customerDAO;
}

public void setCustomerDAO(CustomerDAO customerDAO) {
this.customerDAO = customerDAO;
}
}

Again, the only thing you do differently between this Control class and a "standard" Control class is return null, since the getHandler will write to the response stream for you. Also notice the error handling in the else block. The GetHandler will take care of error handling for us; simply call the getHandler.setErrorMessage() and the message will be included in the resulting XML, regardless of whether there are any records to populate.

The JSP file

The JSP is really no different than the samples given with the Nitobi components. Don't forget to include the taglib declaration at the top!

For this tutorial, I've opted to show the Company Name and Contact Name columns for the ComboBox. This is achieved by including the <ntb:combocolumndefinition> tags. Each tag represents a column that will appear when the menu appears. The selected item will appear in the text field, and the column displayed depends on the datafieldindex attribute. Keep in mind the numbers for the indexes directly correspond to the array of fields in the compressed XML, the first field being '0'.

The datasourceurl attribute will determine where the ComboBox component will go to get its data. It will handle all the XHR requests, paging, populating, and all the other back-end functionality automatically. Also, in using the Nitobi Taglib classes, you no longer need to declare the xmlns, the JavaScript and CSS dependencies, and the bootstrapping. Snippet 6 shows the JSP used for the tutorial.

Snippet 6 - index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="ntb" uri="http://www.nitobi.com" %>

<html>
<head>
<link type="text/css" rel="stylesheet" href="/resources/samples.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Nitobi Spring Tutorial</title>
</head>
<body>

<p class="notes">Start typing a customer name.</p>

<table>
<tr>
<td valign="middle">Customer Name: </td>
<td>
<ntb:combo id="cmbCustomers" mode="classic">
<ntb:combotextbox width="250px" datafieldindex="5"/>

<!-- A list which allows paging and search. The datasourceurl is the address to get the
data to populate the Combo. You will find customers.xml declared as a bean in the dispatch-servlet.xml
file where it is tied into the Spring context. The *.xml suffix reflects the servlet mapping as
defined in the web.xml -->

<ntb:combolist width="360px" height="200px" datasourceurl="customers.xml" pagesize="15">
<ntb:combocolumndefinition width="130px" headerlabel="Customer Name" datafieldindex="5"/>
<ntb:combocolumndefinition width="200px" headerlabel="Contact Name" datafieldindex="2"/>
</ntb:combolist>
</ntb:combo>
</td>
</tr>
</table>

</body>
</html>

Using the Nitobi Taglib, you can write much of your application without writing a single bootstrapping function. Notice there isn't any javascript to be seen on the page. The Java code will output that automatically for you, converting the ntb tags to the the appropriate javascript imports, bootstraps, and namespace delarations. If you want further functionality, you can tie into the events. Doing that will require corresponding custom Javascript, but other than that, it's pretty straight forward.

Conclusion

As you can see, the Nitobi CUI in conjunction with the Java Server libraries and Spring makes a very flexible and powerful combination. The Nitobi Java components fit into Spring MVC without any special treatment, and the same can probably be said about other popular frameworks. The Nitobi Java libraries also include special support for Struts 2 and JSF, which is pretty cool too.

I hope you were able to use this tutorial to help you out. This is my first tutorial, so I hope I haven't left anything out; please let me know if I did.

JessicaShogs

Good work! Thank you very much! I always wanted to write in my blog something like that. Can I take part of your post to my blog? Of course, I will add backlink?

2009-05-10 11:04:46

Arianablew

I love it! That is way cool man! The steps weren’t that complicated too, which is great.

2009-05-13 06:02:58

Eric Buitenhuis

Yes, of course you may take parts of this! Knowledge is open source, but a back link is appreciated.

2009-05-28 04:42:14