Thursday, 17 May 2012

Spatial data with Grails and MongoDB


Recently, I have been making a presentation during TomTom Dev Day about "Rapid web development using Groovy and Grails". Key point of my presentation was live development of simple POI (Point of Interest - for example: bank, restaurant etc) administration system. User has an web interface, where he can add, remove and list places and data is stored in MongoDB to provide spatial indexing of points. To illustrate spatial quering application implements REST webservice that finds POIs near given point. I am going to simplify the project a bit, but it should be enough to get how those technologies are working together.

Project configuration
To start you will need:
- download Grails (I used 2.0.3)
- download MongoDB (I used 2.0.4)
- Spring Tool Suite is optional

Firstly, create a new Grails project. Next step is to configure it to use MongoDB instead of Hibernate in GORM. Let's go to project directory and reconfigure plugins:

grails uninstall-plugin hibernate
grails install-plugin mongodb 1.0.0.RC4

If you are using STS, refresh the project as well as run "Grails Tools - Refresh Dependencies". I figured out that we need also to update configuration file conf/BuildConfig.groovy:

//runtime ":hibernate:$grailsVersion"

I am not sure, but maybe it should be already done by uninstall-plugin command, so possibly it is a bug in Grails. 

Writing the code
Now, let's add domain object - foo.MyPoi:

package foo

class MyPoi {
    String name
    Double longitude
    Double latitude
    List location
    static constraints = { name(blank: false) }
    static mapping = { location geoIndex:true }
}

MyPoi has its name and lon/lat coordinates as well as strictly technical field named "location". MongoDB cannot understand coords in separate Double fields like longitude and latitude - it need special list containg these values. Please also look at the definition of geoIndex - hint for MongoDB to create spatial index.



Now, it's time to add controller foo.MyPoiController. We will use scaffolding to make things easy. We will only need to override save method to update location field based on longitude and latitude (location field as List will be skipped by scaffolding).

package foo

import grails.converters.JSON

class MyPoiController {
    static scaffold = MyPoi

    def save() {
        def myPoi = new MyPoi(params)
        myPoi.location = [
            myPoi.latitude,
            myPoi.longitude
        ]
        myPoi.save()
        redirect(action: 'list')
    }
    def listNearPointAsJson() {
        def point = [
            new Double(params.lat),
            new Double(params.lon)
        ]

        def myPois = MyPoi.findAllByLocationWithinCircle([point, new Double(params.r)])
        render myPois as JSON
    }
}

I have added custom listNearPointAsJson controller action to implement webservice. Notice the beauty of autogenerated MyPoi.findAllByLocationWithinCircle method that allows us to find POIs :)

NOTE: I discovered that Grails is generating HTML5 forms in scaffolded views. Moreover, Double fields are generated as "number" input type. It seems that Chrome has a bug related to validation of values entered in such fields: http://stackoverflow.com/questions/8052962/groovy-grails-float-value-and-html5-number-input - please use Firefox for now. Bug should be corrected soon, but you can also make it work with static scaffolding (generate view gsp files) and make simple correction describe in linked article.


Running our application
Start MongoDB with command like:
mongod.exe --dbpath ../data --rest

Start our Grails project:
grails run-app

Add some places using web interface:













And here is the list:












Let's play with WS to check if spatial indexing works. At first let's get a query for the point that is far away from data we just entered: http://localhost:8080/PoiTest6/myPoi/listNearPointAsJson?lat=51.0&lon=18.0&r=0.5

Result is empty JSON:
[]

Now, let's change the query point to get one of POIs inside radius defined as third url param: http://localhost:8080/PoiTest6/myPoi/listNearPointAsJson?lat=51.0&lon=18.7&r=0.5

Result is as expected:







We can move it closer to second POI in DB: http://localhost:8080/PoiTest6/myPoi/listNearPointAsJson?lat=51.0&lon=19.0&r=0.5 to get both POIs:

[{"class":"foo.MyPoi","id":1,"latitude":51.1,"longitude":19.1,"name":"poi1"},{"class":"foo.MyPoi","id":2,"latitude":51.2,"longitude":19.2,"name":"poi2"}]

Summary
It's all very simple as it is usually with Grails. Integration with MongoDB is also straightforward. Unfortunatelly, there was also a little surprise with Chrome... Anyway, I hope it was interesting.

Sunday, 25 December 2011

Some good books I have read recently...

I did not have much time for programming that year. Apart of my job, of course! I think number of my blog posts confirms that;) Anyway, I read a few programming books in the mean time... and would like to recommend two of them.

Seven Languages in Seven Weeks
Is a "A Pragmatic Guide to Learning Programming Languages" by Bruce A. Tate (see: http://pragprog.com/book/btlang/seven-languages-in-seven-weeks).



It's a journey through seven programming languages, showing how different they can be and to how different programming worlds they belong to.




The book presents:
  • Ruby - modern dynamic, object oriented language used a lot in web development
  • Io - prototype based language that has a lot in common with JavaScript
  • Prolog - logic programming language that is just different from anything other I have ever seen before
  • Scala - getting more and more popular object oriented and functional language for JVM
  • Erlang - functional language designed in Ericsson labs for running mission critical apps
  • Clojure - modern Lisp implementation for JVM
  • Haskell - functional language, but with totally different philosophy than Lisp
From my perspective each of presented languages from the list has some interesting points and each has been choosen for some reason. Author does not concentrate on teaching you details of the language and its syntax, but wants you to understand its philosophy. He wants you to learn how to learn things. He wants you not to limit your thinking by knowing only one potential solution to your problem.
Another point is that after almost seven (like the title hi) years of professional Java development, I was finding myself a little bit closed. But that book certanly opened my eyes. I believe discovering the variety of programming worlds makes me better programmer (and brings more fun!) than learing next 187'th Java framework...
Author puts it that way:
"Java was like having a rich lawyer as a brother. He was fun when he was younger, but now he's a black hole that sucks away all the joy in a 100-mile radius."
—Bruce A. Tate - Seven Languages in Seven Weeks

PostGIS in Action
by Regina O. Obe and Leo S. Hsu is a guide to PostGIS - spatial extension for PostgreSQL database (see: http://www.manning.com/obe/)
It allows you not only to store spatial data as a maps, locations etc but to perform a lot of operations like searching, measuring distance, areas or making geometrical transformations.
I would say this is a must read for GIS developers like me. It is clear that it contains deep technical explanation of spatial data types or, database functions to operate in them.
What is much more important is contains all the GIS background you should (must!) be aware of before you start working with spatial data. It may be surprising for some, but you can not just treat spatial data as coordinates on Euclidean coordinate systems. Earth is not flat! Earth is not a ball neither.
To make you "GIS ready" programmer, authors guide you through things like spatial reference systems or map projections and make you understand the difference between them. They describe which one would be the best for each set of requirements, like for example calculating accurate distances or preserving shapes and angles for visualization.
I should also mention useful information about performance tuning as well as a lot general SQL hints like "do it that way" or "never use" etc. There are also nice bonus chapters like PostreSQL administration guide or SQL tutorial to get you back on track.
Enjoy reading and Happy New Year!

Sunday, 1 August 2010

Python web development for Google App Engine

To start your very first Python web development for GAE, you will need:
To run any application you should have GAE account (http://code.google.com/intl/pl/appengine/). You should also create new application on GAE administration pages.



Hello World project
Now, you can try to generate Hello World application using the Pydev's wizard. Create new project in eclipse - use "Pydev Google App Engine Project". Select Python version to 2.5 and configure interpreter to the one that you installed earlier. Next, you will have to specify path to Google App Engine SDK. On next page, set name for your application (the same as was registered on GAE account admin pages) and choose "Hello Webapp World" template. Within few seconds you should have your Hello World web app generated.



Run Google App Engine Launcher that was installed with SDK. If you are running it first time, configure paths to Python and SDK (Edit -> Preferences). Add your project to the Launcher:



Now, you should be able to run your application locally as well as deploy it to GAE. During deployment you will need to log into your GAE account.



More complex application

As usual, I created my test app as simple Book management application. It is not even CRUD as I had no time to develope update functionality. Application requires user to log in using Google Account before they enter the main page.



There, user will have links to other pages:
  • Book list - when user will be able to display and remove books
  • New book - page with form for adding new entries
  • log out
Project structure:



Projects uses Google Webapp framework that is quite simple, but on the other hand you don't need anything more sophisticated for such simple app;)

Main application code and logic is in books.py:
import os

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from models import Book

from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
def get(self):
user = users.get_current_user()

if user:
template_values = {
'user' : user.nickname(),
'logoutUrl' : users.create_login_url("/")
}
path = os.path.join(os.path.dirname(__file__)+"/templates", 'index.html')
self.response.out.write(template.render(path, template_values))

else:
self.redirect(users.create_login_url(self.request.uri))

class NewBookPage(webapp.RequestHandler):
def get(self):
path = os.path.join(os.path.dirname(__file__)+"/templates", 'new.html')
self.response.out.write(template.render(path, {}))

class ListBooksPage(webapp.RequestHandler):
def get(self):
books = Book.all();
template_values = {
'books' : books,
}

path = os.path.join(os.path.dirname(__file__)+"/templates", 'list.html')
self.response.out.write(template.render(path, template_values))

class SaveBookPage(webapp.RequestHandler):
def post(self):
book = Book();
book.title = self.request.get('title')
book.author = self.request.get('author')
book.put()
self.redirect("/list")

class RemoveBookPage(webapp.RequestHandler):
def get(self):
book = Book.get(self.request.get('key'))
book.delete()
self.redirect("/list")

application = webapp.WSGIApplication(
[('/', MainPage),
('/list', ListBooksPage),
('/save', SaveBookPage),
('/remove', RemoveBookPage),
('/new', NewBookPage)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()
In this module we have main "controller" classes that cover all functionality. Classes are mapped to particular URL. Responsibility of each class is to perform business logic, prepare model and return template that will render the page:
  • MainPage - prepares model for menu, returns index.html template
  • NewBookPage - returns new.html template with form
  • ListBooksPage - selects books from Google Datastore, puts them into model and returns list.html template
  • SaveBookPage- saves book to datastore and redirects view to book list
  • RemoveBookPage - removes book from datastore and redirects view to book list
This module contains also code that maps each controller class to particular URL.

Pages are implemented using Django templated mechanism that is included in SDK. We have a main template base.html that renders html page structure:
<html>
<head>
<link type="text/css" rel="stylesheet" href="/style/main.css" />
</head>
<body>
{% block main %}
{% endblock %}
</body>
</html>
All other templates are extending the main one.

Code of index.html:
{% extends "base.html" %}
{% block main %}
Welcome {{ user }}!
<br/><br/>
Menu:<br/>
<a href="/list">Book list</a><br/>
<a href="/new">New book</a><br/>
<a href="{{ logoutUrl }}">Logout</a>
{% endblock %}
list.html:
{% extends "base.html" %}
{% block main %}
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th></th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td><a href="/remove?key={{ book.key }}">remove</a></td>
</tr>
{% endfor %}
</table>
<br/>
<a href="/">Back</a>
{% endblock %}
new.html:
{% extends "base.html" %}
{% block main %}
<form action="/save" method="POST">
<table>
<tr>
<td>Title:</td>
<td><input type="text" name="title"/></td>
</tr>
<tr>
<td>Author:</td>
<td><input type="text" name="author"/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Add"></td>
</tr>
</table>
</form>
<br/>
<a href="/">Back</a>
{% endblock %}
I used separete main.css file to define look (don't take it seriously hi hi) of the application:
body {
font-family: Verdana, Helvetica, sans-serif;
background-color: #DDDDDD;
}
In app.yaml file, we define that books.py module is responsible for handling all request to /* urls and that /style urls will be exposed as a static resource:
application: python-test-maciekm
version: 1
runtime: python
api_version: 1

handlers:
- url: /style
static_dir: style
- url: /.*
script: books.py
Data model is defined in models.py file:
from google.appengine.ext import db

class Book(db.Model):
title = db.StringProperty()
author = db.StringProperty()
Let's see the result
Such application should be ready to get it started using Launcher and for deployment on GAE. When it starts, you should be able to see application pages in the browser - creating new book:



And the list:



Was it hard? To be honest I really think it was not. Personally, I think it was even easier for me to create GAE application in Python than in Java. In the future, I would be happy to try to use Django instead of Webapp framework and to compare them.

Saturday, 15 May 2010

BPEL compensation handlers with Glassfish ESB example

Introduction
Recently I am involved in integration project based on Glassfish ESB and I needed to get into BPEL technology. There was one thing that really impressed me - compensation handlers.

Why do we need compensation handlers?
Imagine a simple book ordering process defined in BPEL and exposed as a webservice.
Process flow would be:
  • receive needed information: bookId, customerId, creditCardNumber
  • call BookOrderWS - webservice responsible for creating order
  • call PaymentWS - webservice responsible for payment
  • return result of the process
Execution of this process looks very simple until we consider that something may go wrong. Unpredicted exception may occur in the middle of the process. For example payment component may not be working and we will get exception while calling it. Without any fault handling our BPEL process will just stop after customer invoking it via WS will receive fault information. The worst thing is that earlier part of the process was executed properly - order was created by BookOrderWS. So, the book will be posted to customer even though he was not charged for it;)

Possible solutions:
  • use catch clause to react on errors on each webservice call
  • use componsation handlers - for each webservice call define a set of actions that will undo its execution
In first solution we could catch exception while calling PaymentWS and call another webservice that would cancel the order that was created. It would work in our case, but imagine a process that orchestrates ten or more webservice calls - code would be very complicated.

That's why I prefer second solution. No matter in which process step error occurs - all previous steps will know how to undo its operation. Process definition will be much clearer.

Implementation
Now I will show a draft of simple implementation that I prepared to test this concept. During development I was using Glassfish ESB 2.2 and bundled Netbeans IDE 6.7.1.

My application consists of following projects:
  • BookOrderWS
  • PaymentWS
  • BookOrderBPEL
  • BookOrderCA
Webservices
BookOrderWS is responsible for creating and canceling orders. It is implemented as EJB module and consists of one class:
package com.maciekm;

import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.ejb.Stateless;

@WebService()
@Stateless()
public class BookOrderWS {

@WebMethod(operationName = "orderBook")
public long orderBook(@WebParam(name = "bookId") long bookId,
@WebParam(name = "customerId") long customerId) {

long orderId = System.currentTimeMillis();
System.out.println("Order request for bookId: " + bookId + " from customerId: " + customerId + " - created orderId: " + orderId);
return orderId;
}

@WebMethod(operationName = "cancelOrder")
@Oneway
public void cancelOrder(@WebParam(name = "orderId") long orderId) {
System.out.println("Canceling orderId: "+orderId);
}
}
PaymentWS is also EJB module with very simple code:
package com.maciekm;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.ejb.Stateless;
import javax.jws.Oneway;

@WebService()
@Stateless()
public class PaymentWS {

@WebMethod(operationName = "payForOrder")
@Oneway
public void payForOrder(@WebParam(name = "orderId") long orderId,
@WebParam(name = "creditCardNumber") String creditCardNumber) {

System.out.println("Payment received for orderId: " + orderId + ", creditNumber:" + creditCardNumber);
if (creditCardNumber.equals("1234")) {
System.out.println("Payment error!");
throw new RuntimeException("some unexpected errors");
}
System.out.println("Payment completed!");
}
}
Here our mock implementation simulates payment processing. Unpredicted exception will occur for 1234 credit card number.

Process definition and composite application
WSDL of our process is based on very simple input and output schemas:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.netbeans.org/schema/order"
xmlns:tns="http://xml.netbeans.org/schema/order"
elementFormDefault="qualified">
<xsd:element name="order" type="tns:order"/>
<xsd:complexType name="order">
<xsd:sequence>
<xsd:element name="bookId" type="xsd:integer"/>
<xsd:element name="customerId" type="xsd:integer"/>
<xsd:element name="creditCardNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xml.netbeans.org/schema/orderResult"
xmlns:tns="http://xml.netbeans.org/schema/orderResult"
elementFormDefault="qualified">
<xsd:element name="orderResult" type="tns:orderResult"/>
<xsd:complexType name="orderResult">
<xsd:sequence>
<xsd:element name="isCompleted" type="xsd:boolean"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Process diagram:



Code:
<?xml version="1.0" encoding="UTF-8"?>
<process
name="bookOrderBPEL"
targetNamespace="http://enterprise.netbeans.org/bpel/BookOrderBPEL/bookOrderBPEL"
xmlns:tns="http://enterprise.netbeans.org/bpel/BookOrderBPEL/bookOrderBPEL"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
xmlns:sxt="http://www.sun.com/wsbpel/2.0/process/executable/SUNExtension/Trace"
xmlns:sxed="http://www.sun.com/wsbpel/2.0/process/executable/SUNExtension/Editor"
xmlns:sxeh="http://www.sun.com/wsbpel/2.0/process/executable/SUNExtension/ErrorHandling" xmlns:sxed2="http://www.sun.com/wsbpel/2.0/process/executable/SUNExtension/Editor2" xmlns:ns0="http://xml.netbeans.org/schema/order" xmlns:ns1="http://xml.netbeans.org/schema/orderResult">
<import namespace="http://enterprise.netbeans.org/bpel/BookOrderWSServiceWrapper"
location="BookOrderWSServiceWrapper.wsdl" importType="http://schemas.xmlsoap.org/wsdl/"/>

<import namespace="http://maciekm.com/" location="BookOrderWS/wsdl/BookOrderWSService.wsdl"
importType="http://schemas.xmlsoap.org/wsdl/"/>

<import namespace="http://enterprise.netbeans.org/bpel/PaymentWSServiceWrapper" location="PaymentWSServiceWrapper.wsdl"
importType="http://schemas.xmlsoap.org/wsdl/"/>

<import namespace="http://maciekm.com/" location="PaymentWS/wsdl/PaymentWSService.wsdl"
importType="http://schemas.xmlsoap.org/wsdl/"/>

<import namespace="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL"
location="bookOrderProcessWSDL.wsdl" importType="http://schemas.xmlsoap.org/wsdl/"/>

<partnerLinks>
<partnerLink name="OrderProcessPL" xmlns:tns="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL"
partnerLinkType="tns:bookOrderProcessWSDL" myRole="bookOrderProcessWSDLPortTypeRole"/>

<partnerLink name="BookOrderPL" xmlns:tns="http://enterprise.netbeans.org/bpel/BookOrderWSServiceWrapper"
partnerLinkType="tns:BookOrderWSLinkType" partnerRole="BookOrderWSRole"/>

<partnerLink name="PaymentPL" xmlns:tns="http://enterprise.netbeans.org/bpel/PaymentWSServiceWrapper"
partnerLinkType="tns:PaymentWSLinkType" partnerRole="PaymentWSRole"/>

</partnerLinks>
<variables>
<variable name="CancelOrderIn" xmlns:tns="http://maciekm.com/" messageType="tns:cancelOrder"/>
<variable name="OrderBookOut" xmlns:tns="http://maciekm.com/" messageType="tns:orderBookResponse"/>
<variable name="PayForOrderIn" xmlns:tns="http://maciekm.com/" messageType="tns:payForOrder"/>
<variable name="OrderBookIn" xmlns:tns="http://maciekm.com/" messageType="tns:orderBook"/>
<variable name="BookOrderProcessWSDLOperationOut"
xmlns:tns="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL"
messageType="tns:bookOrderProcessWSDLOperationResponse"/>

<variable name="BookOrderProcessWSDLOperationIn"
xmlns:tns="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL"
messageType="tns:bookOrderProcessWSDLOperationRequest"/>

</variables>
<sequence>
<receive name="ReceiveOrder" createInstance="yes" partnerLink="OrderProcessPL"
operation="bookOrderProcessWSDLOperation"
xmlns:tns="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL"
portType="tns:bookOrderProcessWSDLPortType" variable="BookOrderProcessWSDLOperationIn"/>

<scope name="BookOrderScope">
<compensationHandler>
<sequence name="Sequence2s">
<assign name="Assign1">
<copy>
<from>$OrderBookOut.parameters/return</from>
<to>$CancelOrderIn.parameters/orderId</to>
</copy>
</assign>
<invoke name="CancelOrder" partnerLink="BookOrderPL" operation="cancelOrder"
portType="tns:BookOrderWS" inputVariable="CancelOrderIn" xmlns:tns="http://maciekm.com/"/>

</sequence>
</compensationHandler>
<sequence name="Sequence1">
<assign name="AssignToOrder">
<copy>
<from>$BookOrderProcessWSDLOperationIn.part1/ns0:bookId</from>
<to>$OrderBookIn.parameters/bookId</to>
</copy>
<copy>
<from>$BookOrderProcessWSDLOperationIn.part1/ns0:customerId</from>
<to>$OrderBookIn.parameters/customerId</to>
</copy>
</assign>
<invoke name="OrderBook" partnerLink="BookOrderPL" operation="orderBook"
xmlns:tns="http://maciekm.com/" portType="tns:BookOrderWS" inputVariable="OrderBookIn" outputVariable="OrderBookOut"/>

</sequence>
</scope>
<assign name="AssignToPay">
<copy>
<from>$BookOrderProcessWSDLOperationIn.part1/ns0:creditCardNumber</from>
<to>$PayForOrderIn.parameters/creditCardNumber</to>
</copy>
<copy>
<from>$OrderBookOut.parameters/return</from>
<to>$PayForOrderIn.parameters/orderId</to>
</copy>
</assign>
<invoke name="Pay" partnerLink="PaymentPL" operation="payForOrder" xmlns:tns="http://maciekm.com/"
portType="tns:PaymentWS" inputVariable="PayForOrderIn"/>

<assign name="AssignToReply">
<copy>
<from>true()</from>
<to>$BookOrderProcessWSDLOperationOut.part1/ns1:isCompleted</to>
</copy>
</assign>
<reply name="Reply" partnerLink="OrderProcessPL" operation="bookOrderProcessWSDLOperation"
xmlns:tns="http://j2ee.netbeans.org/wsdl/BookOrderBPEL/bookOrderProcessWSDL" portType="tns:bookOrderProcessWSDLPortType"
variable="BookOrderProcessWSDLOperationOut"/>

</sequence>
</process>
As you can see BookOrderScope defines its compensation handler. It calls cancelOrder method of BookOrderWS.

Composite application that connects all modules together:



Testing how it works
Composite application has a testing support in Netbeans IDE. We can use it to create test cases.
Test case one - sending valid order and payment information - processing works properly:
Order request for bookId: 1 from customerId: 2 - created orderId: 1274383922765
Retrieving schema at '', relative to 'file:/PaymentWSService.wsdl'.
Payment received for orderId: 1274383922765, creditNumber:1111
Payment completed!
Test case two - sending valid order, but credit card number that simulates error - order created by first WS is cancelled by compensation handler:
Order request for bookId: 1 from customerId: 2 - created orderId: 1274383962281
Payment received for orderId: 1274383962281, creditNumber:1234
Payment error!
The log message is null.
javax.ejb.EJBException
at com.sun.ejb.containers.BaseContainer.processSystemException(BaseContainer.java:3903)
at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:3803)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:3605)
........................................................................................................................
Canceling orderId: 1274383962281
It works indeed:)

PS. Whole application was just quick test, so forgive me messy code and totally naive business scenario;)

Thursday, 11 February 2010

In-Out to In-Only MEP transformation in ServiceMix ESB's bean component

Recently I needed to write servicemix-bean component that implements following scenario:
  • receives In-Out MEP request
  • sends response to received In-Out request
  • constructs In-Only MEP request and forwards it to another service
I had some problems to get it working and could not find help in the internet. So, when I finally solved that problem I decided to share my solution.

My component:
package org.maciekm;

import java.io.StringReader;
import java.util.Date;

import javax.annotation.Resource;
import javax.jbi.component.ComponentContext;
import javax.jbi.messaging.DeliveryChannel;
import javax.jbi.messaging.ExchangeStatus;
import javax.jbi.messaging.InOnly;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.MessageExchangeFactory;
import javax.jbi.messaging.MessagingException;
import javax.jbi.messaging.NormalizedMessage;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamSource;

import org.apache.servicemix.MessageExchangeListener;
import org.apache.servicemix.client.ServiceMixClient;
import org.apache.servicemix.client.ServiceMixClientFacade;
import org.apache.servicemix.jbi.jaxp.SourceTransformer;

public class MyService implements MessageExchangeListener {

@Resource
private DeliveryChannel channel;

@Resource
private ComponentContext context;

private String targetServiceNS;
private String targetServiceName;

public String getTargetServiceNS() {
return targetServiceNS;
}

public void setTargetServiceNS(String targetServiceNS) {
this.targetServiceNS = targetServiceNS;
}

public String getTargetServiceName() {
return targetServiceName;
}

public void setTargetServiceName(String targetServiceName) {
this.targetServiceName = targetServiceName;
}

public void onMessageExchange(MessageExchange exchange) throws MessagingException {
if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
try {
NormalizedMessage message = exchange.getMessage("in");
Source content = message.getContent();

// getting "in" message of In-Out request
String body = (new SourceTransformer()).toString(content);

// some your custom logic

// sending response to initial In-Out request - place some xml there
String out=".......... some xml ..............";
StreamSource respSource = new StreamSource(new StringReader(out));
message.setContent(respSource);
exchange.setMessage(message, "out");
channel.send(exchange);

// some business logic to prepare message to forward based on original request

// sending In-Only message to service defined by targetServiceNS and
// targetServiceName properties
String forwardOut=".......... some xml ..............";
StreamSource forwardSource = new StreamSource(new StringReader(forwardOut));
MessageExchangeFactory mef =
channel.createExchangeFactoryForService(new QName(targetServiceNS, targetServiceName));
InOnly inOnlyExch = mef.createInOnlyExchange();
NormalizedMessage forwardMessage = inOnlyExch.createMessage();
forwardMessage.setContent(forwardSource);
inOnlyExch.setInMessage(forwardMessage);
ServiceMixClient smc = new ServiceMixClientFacade(context);
smc.send(inOnlyExch);
} catch (TransformerException te) {
// please do it better way in real project ;)
throw new MessagingException(te);
}
}
}
}
Declaration in xbean.xml file:
<bean id="myService" class="org.maciekm.MyService">
<property name="targetServiceNS" value="http://maciekm.org/esb" />
<property name="targetServiceName" value="targetService" />
</bean>

Injected properties are defining target service of In-Only message. Hope it helps some of you:)

Saturday, 30 January 2010

(Almost) RESTful application with Grails and Dojo

Introduction
As REST techniques are getting more and more popular I wanted to give it a try. Of course I wanted to use Grails. For client side I have chosen Dojo javascript framework as I already had some experience with it.

Project setup
During my experiment I used Grails 1.2.0.
Let's start with generating simple project with Book domain object and controller - almost as in Grails tutorial:

grails create-app GrailsRestTest
cd GrailsRestTest
grails create-domain-class book

grails create-controller Book

Only one thing that surprised me was a message: "WARNING: You have not specified a package. It is good practise to place classes". I wanted to make it simple so I just ignored it by writing "y" and pushing enter;)

As in Grails tutorial, I added some sample data to /grails-app/conf/BootStrap.groovy file:
def init = { servletContext ->
new Book(author:"Stephen King",title:"The Shining").save()
new Book(author:"James Patterson",title:"Along Came a Spider").save()
}
Final project structure will be:



RESTful server
We need to create controller that will be performing base CRUD operations and map its methods to REST style URLs.

Edit /grails-app/conf/UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"/book" (controller: "book") {
action = [GET:"list", POST: "create"]
}
"/book/$id" (resource: "book")
}
}
Entry with /book maps GET and POST HTTP requests to this URL to list and create controller methods. Below we map URLs like /book/$id using default grails REST mapping:
  • GET -> show
  • PUT -> update
  • POST -> save (used when you know ID of item you create - not our case;)
  • DELETE -> delete
Implementation of our controller will use GORM for data storage and will communicate with client application using JSON format. Edit /grails-app/controllers/BookController.groovy:

import grails.converters.*

class BookController {
def list = {
response.setHeader("Cache-Control", "no-store")
render Book.list(params) as JSON
}

def show = {
Book b=Book.get(params.id)
render b as JSON
}

def create = {
def json = JSON.parse(request);
def b = new Book(json)
b.save()
response.status = 201
response.setHeader('Location', '/book/'+b.id)
render b as JSON
}

def delete = {
Book b=Book.get(params.id)
b.delete()
render(status: 200)
}
}
I guess this code is quite easy to understand. In create operation we need to send 201 HTTP code and URL to newly created item in Location header. I did not implement save and update methods to simplify my tutorial.

Run this code with grails run-app and point your browser to http://localhost:8080/GrailsRestTest/book. You should get following response:
[{"class":"Book","id":1,"author":"Stephen King","title":"The Shining"},{"class":"Book","id":2,"author":"James Patterson","title":"Along Came a Spider"}]
To get particular item try http://localhost:8080/GrailsRestTest/book/1 - response should be:
{"class":"Book","id":1,"author":"Stephen King","title":"The Shining"}
Client interface with Dojo
Install Dojo with:
grails install-plugin dojo

Add following page to /grails-app/views/gui/books.gsp:
<html>
<head>
<script type="text/javascript" src="<g:createLinkTo file="/js/dojo/dojo-1.3.0/dojo/dojo.js" />" djConfig="parseOnLoad:true, isDebug:false">
<script type="text/javascript" src="<g:createLinkTo file="/js/dojo/dojo-1.3.0/dijit/dijit.js" />"></script>
<style type="text/css">
@import "<g:createLinkTo file="/js/dojo/dojo-1.3.0/dijit/themes/tundra/tundra.css" />";
@import "<g:createLinkTo file="/js/dojo/dojo-1.3.0/dojox/grid/resources/Grid.css" />";
@import "<g:createLinkTo file="/js/dojo/dojo-1.3.0/dojox/grid/resources/tundraGrid.css" />";
</style>
</head>
<body class="tundra">
<script type="text/javascript">
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.data.JsonRestStore");
dojo.require("dijit.Dialog");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.Button");

dojo.addOnLoad(function() {
var restStore = new dojox.data.JsonRestStore({target:"<g:createLink controller="book" />"});

var layout = [
{
field: 'id',
name: 'Id',
width: '50px'
},
{
field: 'title',
name: 'Title',
width: '200px'
},
{
field: 'author',
name: 'Author',
width: '100px'
}];

var grid = new dojox.grid.DataGrid({
store: restStore,
structure: layout,
selectionMode: 'single'
},
document.createElement('div'));

dojo.byId("gridContainer").appendChild(grid.domNode);

grid.startup();

saveBook = function(book) {
restStore.newItem(book);
restStore.save();
// to force grid refresh
grid.sort();
}

removeBook = function() {
var book = grid.selection.getSelected()[0];
if (book !=null && book.id != null) {
restStore.deleteItem(book);
restStore.save();
}
}

showAddForm = function() {
document.getElementById('title').value="";
document.getElementById('author').value="";
dijit.byId('dialog1').show();
}
});
</script>

<div id="gridContainer" style="width: 375px; height: 200px;"></div>
<button dojoType="dijit.form.Button" onclick="showAddForm()">Add book</button>
<button dojoType="dijit.form.Button" onclick="removeBook()">Remove</button>

<div dojoType="dijit.Dialog" id="dialog1" title="Add book"
execute="saveBook(arguments[0]);">
<table>
<tr>
<td><label for="title">Title: </label></td>
<td><input dojoType="dijit.form.TextBox" type="text" name="title" id="title"></td>
</tr>
<tr>
<td><label for="author">Author: </label></td>
<td><input dojoType="dijit.form.TextBox" type="text" name="author" id="author"></td>
</tr>
<tr>
<td colspan="2" align="center">
<button dojoType="dijit.form.Button" type="submit">Save</button></td>
</tr>
</table>
</div>
</body>
</html>
Our client is based on three Dojo components:
  • JsonRestStore - responsible for transparent client-server communication
  • DataGrid - grid presenation for data
  • Dialog - simple popup for adding new book items
After starting application and pointing your browse to http://localhost:8080/GrailsRestTest/gui/books.gsp you should see:



DataGrid asked JsonRestStore for data and it made following request to server:
GET /GrailsRestTest/book/? HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: application/json,application/javascript
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Range: items=0-24
Content-Type: application/json
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8080/GrailsRestTest/gui/books.gsp
Response contains JSON data as presented earlier.

Clicking Add button will show popup that allows creating new items:



Submitting data will result in following request and response:
POST /GrailsRestTest/book/ HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: application/json,application/javascript
Accept-Language: pl,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Transaction: commit
Content-ID:
Content-Type: application/json; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8080/GrailsRestTest/gui/books.gsp
Content-Length: 49
Pragma: no-cache
Cache-Control: no-cache
{"title":"Test author 1","author":"Test title 1"}

HTTP/1.x 201 Created
Server: Apache-Coyote/1.1
Location: /book/3
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 30 Jan 2010 13:59:06 GMT
At that is the moment when I had some problems. You probably noticed commented line with grid.sort() in gsp page. It forces grid and JsonRestStore to reload data from server after saving new item. When you remove this line, you will notice that new item is still added to store and even to the table (without reloading) - but ID of new item is missing. According to Dojo documentation JsonRestStore should pick this ID from Location http header of response. Unfortunatelly it does not. Am I doing something wrong? Or is this a bug? Hints are welcome!

To remove item select it and push Remove button. Dojo will make request similar to:
DELETE /GrailsRestTest/book/8 HTTP/1.1
Host: localhost:8080
This time item is automatically removed from store and grid and any data reloading is not needed.

Conclusion
The title of this article starts with "Almost" as it is not complete application. I did not implement any update operation. Also this ID issue with Dojo makes me a little worried. Application was tested only on Firefox... so to make it "production ready" you should also test (or get it working ;) on other browsers. Anyway, it was fun to try something new...

Tuesday, 10 November 2009

Grails on GAE and persistence

Introduction
In my last post we created very simple Grails project and deployed it on Google Application Engine. Now we will try to extend it to use Google Datastore for persistence.

Configuration
Create a project in the same way as in previous post but ensure you will choose JPA as a persistence provider. Another option is JDO but I simply don't know this technology;)
Next step will be to install gorm-jpa plugin for Grails:
grails install-plugin gorm-jpa

Application goals
We will create very simple one page application to manage list of books. On the top there will be a list of book items. At the bottom there will a form to store new ones.



Implementation
We should create class for our domain object - grails-app\domain\test\Book.groovy:
package test

import javax.persistence.*;

@Entity
class Book implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id

String title

static constraints = {
id visible:false
}
}
We need also web controller that will be responsible for 3 web actions:
  • index - main action listing all books
  • save - adding new book
  • remove - removing book
Add following code to grails-app\controllers\BookController.groovy:
import test.*

class BookController {
def index = {
[books : Book.list( params )]
}

def save = {
Book b=new Book(params)
b.save()
redirect(action : index)
}

def remove = {
Book b=Book.get(params.id)
b.delete()
redirect(action : index)
}
}
Finally add GSP page (grails-app\views\book\index.gsp) that will present data and invoke particular actions of our controller:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<title></title>
</head>
<body>
<h1>Books:</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th></th>
</tr>
</thead>

<g:each in="${books}" status="i" var="book">
<tr>
<td>${book.id}</td>
<td>${book.title}</td>
<td><g:link action="remove" id="${book.id}">remove</g:link> </td>
</tr>
</g:each>
</table>
<h1>New:</h1>
<g:form action="save">
<table>
<tr>
<td>Title</td>
<td><g:textField name="title" /></td>
</tr>
<tr>
<td colspan="2"><input type="submit" /></td>
</tr>
</table>
</g:form>
</body>
</html>
Deploy the application just as we did it in last post. Point the browser to /book resource of your web app. You should see our page running.

Now, you can also browse datastore on GAE account pages:

Saturday, 7 November 2009

Deploying Grails application on Google Application Engine

Introduction
Some time ago it was announced that Google Application Engine supports Grails framework. As a great fan of Groovy and Grails I wanted to give it a try. I was also very curious to see how GAE works. In this article I would like to present simple "Hello World" project and its deployment process.

My example uses:
  • Grails 1.1.1 (http://www.grails.org)
  • GAE SDK 1.2.6 for Java (http://code.google.com/intl/pl/appengine/downloads.html#Google_App_Engine_SDK_for_Java)
  • Java 1.6
Configuration
As a first step please download required software and install it. To configure it properly you need to set up:
  • variable GRAILS_HOME to directory where you installed Grails
  • variable APPENGINE_HOME to directory where you installed GAE SDK
  • add GRAILS_HOME\bin directory to PATH variable
Creating GAE account
Go to GAE website (http://appengine.google.com) and create an account. Log in and add new application to your account. Specify application name - I will use "gae-grails-maciek-test".

Creating simple project
Go the your workspace directory and type: grails create-app GoogleGrailsTest
Script should create your project subdirectory - GoogleGrailsTest. You can import your project to Eclipse or another IDE.



Please modify GoogleGrailsTest\grails-app\conf\Config.groovy and add following line:
google.appengine.application="gae-grails-maciek-test"

Of course you should use your GAE application name. Go to project directory and install grails GAE plugin:
grails install-plugin app-engine

You will be asked if you want to use JPA or JDO for persistence. It is not important at all for our case.

To run you project locally on your computer type: grails app-engine run
You should be able to access it at http://localhost:8080



I must confess I noticed a strange stack trace in log files, but application was still running properly...

Deployment
Type:
grails set-version 1
grails app-engine package

First deployment should be done with:
$APPENGINE_HOME/bin/appcfg.cmd update ./target/war

Later when you just update application on GAE sever you can just type:
grails app-engine deploy

During deployment process you will be asked about your google account email and password. I noticed strange bug during authentication process. When script asks you for email just push Enter and go to the next line and enter it there. Password can be entered normally. Without this trick I was not able to authenticate properly.

Now you should be able to access your application at http://your-app-name.appspot.com - in my case on http://gae-grails-maciek-test.appspot.com/

Application can be monitored on GAE pages after logging in to your account:




Sunday, 6 September 2009

Partial rendering with Spring MVC and Spring Javascript

Introduction
Almost all modern web pages uses many AJAX techniques. One of them is partial rendering - it allows user to rerender only part of a page instead of its full contents.

Main benefits are:
  • less response size sent via network
  • less time needed to show new page contents
  • less application server load
  • better user experience
Recently I was using partial rendering in JSF application with Ajax4JSF framework. To have wider perspecitve I was going to try another out of the box solution from lightweight stack like Spring MVC, Spring Javascript and Apache Tiles.

Application goals
Test application has a page layout configured with Tiles. Pages consist of menu on the left side and main contents on the right. Navigation links in menu are used to choose page contents for right side - it will be rerendered (Ajax) after each click on menu item.

Main page (rendering time is just to show ajax behaviour):


Page after ajax rerendering triggered by click in menu:


To prepare my sample application I used:
  • Maven 2.0.9
  • Java 1.6.0_12
  • Spring 2.5.6
  • Spring Javascript 2.0.5
  • Apache Tiles 2.1.0
Project structure
I used typical maven structure for the project:


























All project dependencies are configured in maven's pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.maciekm</groupId>
<artifactId>spring-js-demo</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>spring-js-demo Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>org.springframework.js</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tiles</groupId>
<artifactId>tiles-jsp</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>spring-js-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Web application configuration
At first we need to configure our web app in web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<servlet>
<servlet-name>springjsdemo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<init-param>
<param-name>allowedResourcePaths</param-name>
<param-value>/**/*.css,/**/*.gif,/**/*.ico,/**/*.jpeg,/**/*.jpg,/**/*.js,/**/*.png,META-INF/**/*.css,META-INF/**/*.gif,META-INF/**/*.ico,META-INF/**/*.jpeg,META-INF/**/*.jpg,META-INF/**/*.js,META-INF/**/*.png,META-INF/**/dojo/resources/blank.html,/dojo/resources/blank.html</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springjsdemo</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
</web-app>
Springjsdemo servlet is used to start spring web application context and as a front controller in our application. Resources Servlet is necessary for Spring Javascript. characterEncodingFilter is important when you want to use national characters on pages fragments rerendered by Ajax.

We also have to setup application context - springjsdemo-servlet.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="org.maciekm.springjsdemo"/>
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles/pages.xml</value>
</list>
</property>
</bean>
<bean id="tilesViewResolver" class="org.springframework.js.ajax.AjaxUrlBasedViewResolver">
<property name="viewClass" value="org.springframework.js.ajax.tiles2.AjaxTilesView"/>
</bean>
</beans>
We configured Spring MVC to work with Apache Tiles. Spring MVC controller will be found and initialized by component scanning.

Pages layout
Tiles layouts are configured in pages.xml:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
"http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
<definition name="welcome" template="/WEB-INF/jsp/template.jsp">
<put-attribute name="menu" value="/WEB-INF/jsp/menu.jsp"/>
<put-attribute name="body" value="/WEB-INF/jsp/welcome.jsp"/>
</definition>
<definition name="page1" extends="welcome">
<put-attribute name="body" value="/WEB-INF/jsp/page1.jsp"/>
</definition>
<definition name="page2" extends="welcome">
<put-attribute name="body" value="/WEB-INF/jsp/page2.jsp"/>
</definition>
</tiles-definitions>
Idea of using Tiles is not just to define page layouts. It helps us to define logical structure of pages so we will be able to rerender its parts.

Our layout template is in template.jsp and we configured 3 views based on it: welcome, page1 and page2. Each view consist of menu and body attributes.

Here we have template.jsp source:
<%@ page pageEncoding="UTF-8" %>
<%@ include file="include.jsp" %>
<html>
<head>
<title>Spring JS Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" >
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="-1">
<meta http-equiv="Content-Language" content="pl">
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate, Post-Check=0, Pre-Check=0">
<script type="text/javascript" src="<c:url value="/resources/dojo/dojo.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring.js" />"> </script>
<script type="text/javascript" src="<c:url value="/resources/spring/Spring-Dojo.js" />"> </script>
</head>
<body>
<table cellpadding="5" cellspacing="5" border="1" width="100%">
<tr>
<td id="left" width="150px"><tiles:insertAttribute name="menu" /></td>
<td id="right"><tiles:insertAttribute name="body" /></td>
</tr>
</table>
</body>
</html>
JS files needed for Spring Javascript have been included.

Web pages
The core point of our application is menu. It contains links that will allow you to choose body of the web page and rerender it using Ajax.
<%@ page pageEncoding="UTF-8" %>
<%@ include file="include.jsp" %>
&nbsp;&nbsp;&nbsp;&nbsp;Menu:
<br/>
<br/>
<ul>
<li>
<form action="<c:url value='/page1.htm'/>" method="POST" id="page1Form" style="padding: 0px;margin: 0px;"></form>
<a id="page1" href="#">Page 1</a>
</li>
<li>
<form action="<c:url value='/page2.htm'/>" method="POST" id="page2Form" style="padding: 0px;margin: 0px;"></form>
<a id="page2" href="#">Page 2</a>
</li>
</ul>
<script type="text/javascript">
Spring.addDecoration(new Spring.AjaxEventDecoration({
elementId: "page1",
formId: "page1Form",
event: "onclick",
params: {fragments: "body"}
}));
Spring.addDecoration(new Spring.AjaxEventDecoration({
elementId: "page2",
formId: "page2Form",
event: "onclick",
params: {fragments: "body"}
}));
</script>
<br/><br/>
Rendering time:<br/><%=new java.util.Date()%>
Each link is "decorated" with special Spring Javascript code. AjaxEventDecoration will make Ajax request to server to ask for the page configured in form definition. However, it will ask only for specified page fragment - body.
Name of this fragment must be the same as name of Tiles attribute in layout of requested page.
On both current page as well as loaded fragment we need to have a div element with ID same as fragment name - body. Spring Javascript will be able to replace its old contents with new that just has been loaded from server.

Note: I used POST forms to define links becouse I had some problems with GET requests and cacheing in IE8. However it is not a recommended solution in a serious project. You should consider using GET with random parameters added just to avoid caching problem.

Default page body is defined in welcome.jsp as a part of welcome tiles view:
<%@ page pageEncoding="UTF-8" %>
<%@ include file="include.jsp" %>
<div id="body">
Main area
</div>
As I said, it contains body div as an container for ajax rerendering with its initial value.
Below we have alternative pages that can be selected from menu.

page1.jsp:
<%@ page pageEncoding="UTF-8" %>
<%@ include file="include.jsp" %>
<div id="body">
This is page 1. Rendering time: <%=new java.util.Date()%>
</div>
And page2.jsp:
<%@ page pageEncoding="UTF-8" %>
<%@ include file="include.jsp" %>
<div id="body">
This is page 2. Rendering time: <%=new java.util.Date()%>
</div>
We need also some less important jsp pages to make our application running.
We use include.jsp just to include some TLDs:
<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
index.jsp is used only to redirect us from root view to our welcome page:
<%@ include file="/WEB-INF/jsp/include.jsp" %>
<c:redirect url="welcome.htm"/>
Spring MVC controller
Of course we need MVC controller to map our views to urls. It our simple case it does not contain any business logic.

We map:
  • /welcome.htm -> welcome view
  • /page1.htm -> page1 view
  • /page2.html -> page2 view
Source code:
package org.maciekm.springjsdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {

@RequestMapping("/welcome.htm")
public String welcomePage() {
return "welcome";
}

@RequestMapping("/page1.htm")
public String page1() {
return "page1";
}

@RequestMapping("/page2.htm")
public String page2() {
return "page2";
}
}
Running and testing
To run application type mvn jetty:run from console in main project directory. It should start Jetty server and deploy our webapp to http://localhost:8080/spring-js-demo. Point your browser to this url and test links in menu.

Hopefully our Ajax partial rendering works:)