Category Archives: Spring MVC

Configuring Spring MVC for AJAX to enable SEO

AJAX based web applications always have challenges with Search engines. The fundamental aspect of AJAX is the content bound to AJAX components are not statically bound when the page loads, but are partially rendered as and when a page events occur and loaded in Javascript DOM tree. Search engines like Google and Bing only indexes static contents.

One of our applications has been built using modern web technologies and was completely Ajax based. We extensively used Backbone.js and other AJAX based technologies. And our application was not optimized for Search engines. As our application was built around Spring MVC so as a first step, I wanted to explore if Spring supports SEO. I found a blog from MKYong Spring 3 MVC ContentNegotiatingViewResolver example. We tried this example and it was slightly complicated to setup our application using this technique.

On further research we found an interesting article in Google Webmasters link. As per this link for every request if we pass a “_escaped_fragment_” the Search Engine are capable of indexing these links. In order enable this in a Spring MVC application; we need to use a framework like Phantom.js. Phantom.js has a capability where in it will take all the data in a DOM tree and output HTML content.

As a 1st step create a Phantom.js script as below,

var page = require('webpage').create();
var system = require('system');

// Open the page
page.open(system.args[1], function (status) {
console.log(page.content);
phantom.exit();
});

We need to execute this code from Java and pass the server returned content to this code as below,

public static String contentBuilderHelper(String redirectUrl){
pbCommand.add("phantom-server.js");
pbCommand.add(redirectUrl);
ProcessBuilder builder = new ProcessBuilder(pbCommand);

final Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
output = output + line;
}
return output;
}

Configure Spring MVC component is we need to create a custom filter as below,

public class CrawlerFilter implements Filter{
...
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if(httpRequest.getParameter("_escaped_fragment_")!=null){
String redirectUrl = httpRequest.getRequestURL().toString()+"#!"+httpRequest.getParameter("_escaped_fragment_");
redirectUrl = URLDecoder.decode(redirectUrl,"UTF-8");
System.out.println(redirectUrl);
try {
//Get content from the web
String content = contentBuilderHelper(redirectUrl);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.write(content);
out.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chain.doFilter(request, response);
}
...
}

Configuring filter in web.xml

<filter>
<filter-name>GoSmarter Crawler Filter</filter-name>
<filter-class>com.example.filter.CrawlerFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>Crawler Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Finally, we also need to configure Phantom.js framework in Tomcat server.

I hope this blog helped you.

Advertisements

JUnit testing REST Services and Spring MVC

For people in hurry get the latest code in Github and run “mvn test”

Introduction

There is a good comparison of Webservices and REST here. REST Services a point-to-point communication over HTTP using XML. There are couple of good discussion around this, in this Stackoverflow article and Toolbox article.

Spring MVC is a good framework to develop REST Services. In this blog I will show you how JUnit test REST Services. I started trying out few of materials around spring-test-mvc and testing Spring MVC REST Services. The GET was working, but the POST was not working. You can see that in the JUnit test in the sample. Next I started trying MockHttpServletRequest/ MockHttpServletResponse there is a good material in Stacktrace on JUnit testing Spring MVC and REST Services. I followed the steps, but it still did not work. It was throwing an exception related to PropertyEditor. There was a good article on how to get this error fixed. Once I put this fix it started working.

Details: JUnit for Spring MVC and REST Services

In this example, we developed a REST Service and Spring MVC, which has 5 operations, getAll, findbyId, create, update, delete and we write JUnit test to call each operations and assert for the expected value.

Based on TDD, we write the test first and for all the 5 methods and assert for expected values as below,

@Test
public void testGetLoanRequest() throws Exception {
LoanRequest loanRequest1 = new LoanRequest();
loanRequest1.setId(2);
ibatisTemplate.insert("GoSmarter.loanRequestInsert", loanRequest1);
requestMock.setMethod("GET");
requestMock.setRequestURI("/loanrequests/2");
handlerAdapter.handle(requestMock, responseMock, loanRequestController);
logger.debug(responseMock.getContentAsString());
LoanRequest loanRequest = mapper.readValue(responseMock.getContentAsString(), LoanRequest.class);
assertNotNull(loanRequest);
assertEquals(loanRequest.getId(), Integer.valueOf(2));
}

@Test
public void testListLoanRequest() throws Exception {
LoanRequest loanRequest1 = new LoanRequest();
ibatisTemplate.insert("GoSmarter.loanRequestInsert", loanRequest1);
loanRequest1 = new LoanRequest();
loanRequest1.setId(2);
ibatisTemplate.insert("GoSmarter.loanRequestInsert", loanRequest1);
requestMock.setMethod("GET");
requestMock.setRequestURI("/loanrequests");
handlerAdapter.handle(requestMock, responseMock, loanRequestController);
List loanRequests = mapper.readValue(responseMock.getContentAsString(), List.class);
assertNotNull(loanRequests);
assertEquals(loanRequests.size(), 2);
}

@Test
public void testCreateLoanRequest() throws Exception {
requestMock.setMethod("POST");
String jsonPcUser = mapper.writeValueAsString(loanRequest);
logger.debug(jsonPcUser);
requestMock.setRequestURI("/loanrequests/create/" + jsonPcUser );
handlerAdapter.handle(requestMock, responseMock, loanRequestController);
logger.debug(responseMock.getContentAsString());
assertEquals(responseMock.getContentAsString(), "true");
}

@Test
public void testListLoanUpdate() throws Exception {
requestMock.setMethod("POST");
LoanRequest loanRequest1 = new LoanRequest();
ibatisTemplate.insert("GoSmarter.loanRequestInsert", loanRequest1);
loanRequest1.setCustomerName("krishna prasad");
String jsonPcUser = mapper.writeValueAsString(loanRequest1);
logger.debug(jsonPcUser);
requestMock.setRequestURI("/loanrequests/update/" + jsonPcUser );
handlerAdapter.handle(requestMock, responseMock, loanRequestController);
logger.debug(responseMock.getContentAsString());
assertEquals(responseMock.getContentAsString(), "true");
LoanRequest loanRequest2 = (LoanRequest) ibatisTemplate.queryForObject(
"GoSmarter.loanRequestDetails", 1);
assertEquals(loanRequest2.getCustomerName(), "krishna prasad");
}

@Test
public void testListLoanDelete() throws Exception {
LoanRequest loanRequest1 = new LoanRequest();
ibatisTemplate.insert("GoSmarter.loanRequestInsert", loanRequest1);
requestMock.setMethod("POST");
requestMock.setRequestURI("/loanrequests/delete/1" );
handlerAdapter.handle(requestMock, responseMock, loanRequestController);
List loanRequests = (List) ibatisTemplate
.queryForList("GoSmarter.loanRequestList");
assertEquals(loanRequests.size(), 0);
}

If you see we need to setup the AnnotationMethodHandlerAdapter and setup MappingJacksonHttpMessageConverter. as below,


@Before
public void setUp() {
requestMock = new MockHttpServletRequest();
requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
requestMock.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, Boolean.FALSE);

responseMock = new MockHttpServletResponse();

handlerAdapter = new AnnotationMethodHandlerAdapter();
HttpMessageConverter[] messageConverters = {new MappingJacksonHttpMessageConverter()};
handlerAdapter.setMessageConverters(messageConverters);

mapper = new ObjectMapper();
loanRequest = new LoanRequest();
}

Once we do the setup, we need to add a InitBinder method in the controller as below,

public class LoanRequestController {
@InitBinder
public void initBinder(WebDataBinder b) {
b.registerCustomEditor(LoanRequest.class, new LoanRequestEditor());
}
}

public class LoanRequestEditor extends PropertyEditorSupport {
ObjectMapper mapper = new ObjectMapper();

@Override
public void setAsText(String text) throws IllegalArgumentException {
LoanRequest obj = mapper.readValue(text, LoanRequest.class);
setValue(obj);
}

@Override
public String getAsText() {
return getValue().toString();
}
}

Conclusion

In the above example, we used MockHttpServletRequest and MockHttpServletResponse to do Junit test of REST Services and Spring MVC.

I hope it helped.

Responsive Web Design using Twitter Bootstrap, Spring MVC

Introduction

Responsive Web Design is a new way of building web application. Once you build a application using Responsive Web Design, you will be easily able to make this web application work on any device including Mobile and Handheld devices. Twitter the company open sourced their Twitter Bootstrap framework which supports Responsive Web Design(RWD). Kickstrap is another variant of Twitter Bootstrap. In this blog, I will demonstrate how we will build a Spring MVC based application that uses jquery-tmpl to build a JSON based RWD.

The use case we cover is a simple Airline reservation system, where in for a given origin, destination, the start and end date, we return all the flights. When we select a flight, we show all the deals in the target location.

For people in hurry get the code and the steps from @ Github.

Responsive Web Design

There are 3 key technical features that are heart of Responsive Web design:

Flexible grid-based layout: When you are viewing the page on a mobile devices, when you change the device to landscape or portrait, the page layout automatically adjusts and flows to display within the layout, this is Flexible grid-based layout. In Twitter Bootstrap, it can be achieved using CSS tags as below,

<div class="row-fluid"><!-- put some HTML stuff --></div>

Flexible images: dynamic resizing of images

Media queries: This is CSS3 feature, where in the CSS is returned to the Browser based on querying the Media device. Typical HTML tag you use for this as below,

<!-- for iPad, this is how the media query looks like -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">

Spring MVC and Twitter Bootstrap

The overall data flow is as below,

Responsive Web Design using Twitter Bootstrap, Spring MVC

Responsive Web Design using Twitter Bootstrap, Spring MVC

In this example we build a Single Page Website using Twitter Bootstrap and JQuery-tmpl. On the frontend side, the data is submitted as below,


$('#searchResults').click(function(){
var origin =  $("#origin option:selected").val();
var destination = $("#destination option:selected").val();
var startDate= $("#startDate").val();
var endDate = $("#endDate").val();

$.get("resources/datatemplates/flightList.html", function (template){
$.get("/air/searchResultsJson?leavingFrom=" + origin + "&goingTo=" + destination + "&startDate=" + startDate + "&endDate=" + endDate, function (data){
$("#dataRegion").html("");
$.tmpl(template, data).appendTo("#dataRegion");
});
});
return false;
}

This executes a JQuery and gets the list of flights in the form as JSon objects.

The JQuery-tmpl plugin is used to bind the flightList.html to achieve Single Page Webpage design. The flightList.html looks as below,

<tr>
<td>${startTime}</td>
<td>${startAirport}</td>
<td>${endTime}</td>
<td><a href="#" onclick="return getDetails('${endAirport}')">${endAirport}</a></td>
</tr>

On the Spring MVC side, we need to add the Maven dependency and call the method, refer this link for more details.

The controller code looks as below,

@RequestMapping(value = "searchResultsJson", method = RequestMethod.GET)
public @ResponseBody
List searchResultsJson(@RequestParam String leavingFrom,
@RequestParam String goingTo, @RequestParam String startDate,
@RequestParam String endDate) {
Form form = new Form();

form.setOrigin(leavingFrom);
form.setDestination(goingTo);
form.setStartDate(startDate);
form.setReturnDate(endDate);

return locationService.selectFlights(form);
}

In the above example @ResponseBody help in returning the JSon response to the client.

Conclusion

In this blog I demonstrated, how we can build a web application, that can be adapted to work on any device. It also show how to return JSon response from a Spring MVC based web application.

I hope it helps you.

Setting up a jPetstore Spring MVC application with SQLFire

In this section I will be giving a brief introduction to SQLFire. And I will quickly demonstrate petstore ecommerce application built using Spring MVC, QueryDsl and SQLFire as the backend database.

SQLFire is a distributed database from VMware which can be used to store large amount of relational data using commodity computers and it is one of their peices of puzzle for BigData initiative. It supports JDBC drivers and Peer drivers. In this session, I will create a 2 cluster servers. For more information visit the documentation.

To start the demo,

  • Download SQLFire
  • Download the source code and unzip it
  • Run the command “java –jar vFabric_SQLFire_102_Installer.jar”
  • Start 2 node clusters by doing the following,
    • Under the directory where SQLFire folder was installed create another two directories: server1 and server2.
    • Using sqlf command start two SQLFire server instances that should reside inside the folders that were just created:
      • bin\sqlf server start -dir=server1 -client-port=1527 -mcast-port=12333
      • bin\sqlf server start -dir=server2 -client-port=1528 -mcast-port=12333
  • Run the sqlfire client by executing
    • bin\sqlf
    • connect client ‘localhost:1527’;
    • run ‘<sourcecode download location>\jpetstore\db\ sqlfire\jpetstore-sqlfire-dataload.sql’
    • run ‘<sourcecode download location>\jpetstore\db\ sqlfire\jpetstore-sqlfire-schema.sql’
  • Install maven on your machine and setup the path

Open pom.xml and replace

<dependency>
  <groupId>com.vmware.sqlfire</groupId>
  <artifactId>sqlfire-client</artifactId>
  <version>1.0.0-Beta</version>
</dependency>

With

<dependency>
  <groupId>com.vmware.sqlfire</groupId>
  <artifactId>sqlfireclient</artifactId>
  <version>1.0.2</version>
</dependency>

And in the repositories block add,

<repository>
  <id>gemstone</id>
  <name>Release bundles for SQLFire and GemFire</name>
  <url>http://dist.gemstone.com.s3.amazonaws.com/maven/release</url>
</repository>

Add a new block under project element,

<pluginRepositories>
  <pluginRepository>
    <id>gemstone</id>
    <name>Release bundles for SQLFire and GemFire</name>
    <url>http://dist.gemstone.com.s3.amazonaws.com/maven/release</url>
  </pluginRepository>
</pluginRepositories>

Run ‘mvn tomcat:run’

Now open the browser and type ‘http://localhost:8080/jpetstore’

Volla you have the application up and running. Open eclipse IDE or STS IDE, import maven project into the IDE to see how the application has been developed. You can also see how SQLFire as been used and the JDBC connection.