Sunday, August 2, 2009

Spring Web Service

Spring-WS supports the contract-first development style that means we start with the WSDL contract and use Java to implement said contract.

We will write a sample code to demonstrate how to write contract-first Web services, that is, developing web services that start with the XML Schema/WSDL contract first followed by the Java code second.

Let us take an example where any service provider maintain user database and provides services for updating user information, providing user information (user profile) and authenticating user. Let say user profile has login id, first name, last name, email and address. We want to provide services like AuthenticateUser, UpdateUserProfile, GetUserProfile.

Let us follow the following steps -

1. So, first step would be to create XSD (let us name it as user.xsd). Go through the below schema, it is self-explanatory

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.mysite.com/user"
xmlns:tns="http://www.mysite.com/user"
elementFormDefault="qualified">

<element name="AuthenticateUser">
<complexType>
<sequence>
<element name="LoginId" type="string" />
<element name="Password" type="string" />
</sequence>
</complexType>
</element>


<element name="UpdateUserProfile">
<complexType>
<all>
<element name="Profile" type="tns:UserProfile" />
<element name="LoginId" type="string" />
</all>
</complexType>
</element>

<element name="GetUserProfile">
<complexType>
<sequence>
<element name="LoginId" type="string" />
</sequence>
</complexType>
</element>

<complexType name="UserProfile">
<sequence>
<element name="loginId" type="string" />
<element name="firstName" type="string" />
<element name="lastName" type="string" />
<element name="emailId" type="string" />
<element name="address" type="string" />
</sequence>
</complexType>

<element name="GetUserProfileResponse" type="tns:UserProfile"/>

<element name="AuthenticateUserResponse" type="int" />

<element name="UpdateProfileResponse" type="int" />

</schema>

2. Now that the schema is created, we will generate supporting classes. We will use JAXB to generate the classes. Download JAXB and unzip it. Use the following command to generate the classes -

%JAXB_HOME%\bin\xjc.bat -d . -p test.schema ./config/user.xsd

The target package is test.schema and schema is under config directory. This would generate the classes based on above user schema.

OR we can use the following build.xml using "ant generate"

<?xml version="1.0"?>
<project name="spring-ws-user-ws-server" basedir="." default="default">
<path id="classpath">
<fileset dir="C:\jaxb-ri-20090206\lib">
<include name="**/*.jar"/>
</fileset>
</path>


<target name="generate">

<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask" classpathref="classpath" />

<xjc destdir="./" package="test.schema" >
<schema dir="./config" includes="user.xsd"/>
</xjc>

</target>

</project>


3. Implementing the Endpoint - In Spring-WS, we implement Endpoints to handle the incoming XML messages. There are two flavors of endpoints: message endpoints and payload endpoints. Message endpoints give access to the entire XML message, including SOAP headers. Typically, the endpoint will only be interested in the payload of the message that is the contents of the SOAP body. In that case, creating a payload endpoint makes more sense.

To handle XML we can use various marshalling techniques like JAXB, Castor,XStream etc apart from JDOM, DOM, dom4j, XOM, SAX,and StAX. We will use JAXB marshalling technique.

package test.ws;

import javax.xml.bind.JAXBElement;

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;

import test.schema.AuthenticateUser;
import test.schema.GetUserProfile;
import test.schema.ObjectFactory;
import test.schema.UpdateUserProfile;
import test.schema.UserProfile;
import test.service.IUserService;

@Endpoint
public class UserEndPoint {

IUserService userService;

private ObjectFactory objectFactory = new ObjectFactory();

public UserEndPoint(IUserService userService) {
this.userService = userService;
}

@PayloadRoot(localPart = "AuthenticateUser", namespace = "http://www.mysite.com/user")
public JAXBElement<Integer> authenticateUser(AuthenticateUser request) {
System.out.println(request.getLoginId() + " " + request.getPassword());
int result = userService.authenticateUser(request.getLoginId(), request
.getPassword());
return objectFactory.createAuthenticateUserResponse(result);
}

@PayloadRoot(localPart = "UpdateUserProfile", namespace = "http://www.mysite.com/user")
public JAXBElement<Integer> updateUserProfile(UpdateUserProfile request) {
int result = userService.updateUserProfile(request.getLoginId(),
request.getProfile());
return objectFactory.createUpdateProfileResponse(result);
}

@PayloadRoot(localPart = "GetUserProfile", namespace = "http://www.mysite.com/user")
public JAXBElement<UserProfile> getUserProfile(GetUserProfile request) {
UserProfile userProfile = userService.getUserProfile(request
.getLoginId());
return objectFactory.createGetUserProfileResponse(userProfile);
}
}


4. We have injected userService to above end point, the service interface looks like -

package test.service;

import test.schema.UserProfile;

public interface IUserService {
public int authenticateUser(String loginId, String password);

public int updateUserProfile(String loginId, UserProfile profile);

public UserProfile getUserProfile(String loginId);
}

5. Routing the Message to the Endpoint - I have used annotation to configure Endpoint and PayloadRoot. See @Endpoint and @PayloadRoot. let us define applicationContext-ws.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:oxm="http://www.springframework.org/schema/oxm"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-1.5.xsd">


<bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
<property name="xsd" value="/WEB-INF/user.xsd"/>
</bean>

<bean id="userEndPoint" class="test.ws.UserEndPoint">
<constructor-arg ref="userService"/>
</bean>

<oxm:jaxb2-marshaller id="marshaller" contextPath="test.schema"/>

<bean id="annotationMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
</bean>

<sws:marshalling-endpoints/>
</beans>

Also we define applicationContext.xml where we define the userService bean

<?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:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:annotation-config/>

<bean id="userService" class="test.service.UserService" />
</beans>


6. Publishing the WSDL - we don't have to write a WSDL; Spring-WS can generate itself. This is how ws-servlet.xml looks like

<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">

<bean id="user" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
<property name="schema" ref="schema"/>
<property name="portTypeName" value="User"/>
<property name="locationUri" value="/userService"/>
</bean>

</beans>

- The bean id determines the URL where the WSDL can be retrieved. Here the bean id is user that means the WSDL can be retrieved as user.wsdl in the servlet context. The full URL will be like http://localhost:8080/UserService/user.wsdl.

- The schema property refers to the user schema defined in point 1. We will place the schema in the WEB-INF directory of the application.

- We will define the WSDL port type to be User

- We set the location where the service can be reached: /UserService/

7. Now let us write web.xml, which looks like

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Spring WS</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-ws.xml
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

<mime-mapping>
<extension>xsd</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

8. Now let us deploy the web application.
- Copy user.xsd, web.xml, applicationContext-ws.xml, applicationContext.xml, ws-servlet.xml under /WEB-INF/
- Copy all required libs under WEB-INF/lib - spring-ws-1.5.6-all.jar, spring-ws-core-1.5.6.jar, spring-webmvc.jar, spring-oxm-1.5.6.jar, spring.jar, commons-logging-1.0.4.jar, wsdl4j-1.6.1.jar, XmlSchema-1.4.3.jar
- Copy the UserEndPoint class, all supporting classes and service classes under WEB-INF/class and restart tomcat


Create WAR and deploy under Tomcat. We have successfully deployed the webservice. Check the WSDL using http://localhost:8080/UserService/user.wsdl

We will next write the dummy client. But after a short break :)

Wednesday, July 22, 2009

Spring Remoting

Spring provides a special remoting strategy which allows for Java serialization via HTTP, supporting any Java interface. The corresponding support classes are HttpInvokerProxyFactoryBean and HttpInvokerServiceExporter.

I will write a simple service and a client to demonstrate the use of Spring HTTP Remoting. Let us follow the following steps -

1. Write an interface let say it is IMyTestService

package com.test.server;

public interface IMyTestService {
public int authenticateUser (String login, String password);
}

2. Write implementation implementing the above interface

package com.test.server;

public class MyTestService implements IMyTestService {

public int authenticateUser(String login, String password) {
System.out.println("MyTestService.authenticateUser called : " + login
+ " = " + password);
return 0;
}

}

3. Write applicationContext.xml to define the above mentioned service.

<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="myService" class="com.test.server.MyTestService" />
</beans>


4. Write web.xml

<?xml version="1.0" encoding="UTF-8" ?>
<web-app id="testwebapp" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Sample Web Application</display-name>
<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>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/myService</url-pattern>
</servlet-mapping>
</web-app>

So, here any request with url pattern /myService would be handled by servlet name "remoting". The servlet name "remoting" is mapped to servlet class org.springframework.web.servlet.DispatcherServlet

5. Now we will write servlet context configuration file. Since the servlet name is step 4 is given as "remoting" so we will write remoting-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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean name="/myService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="myService" />
<property name="serviceInterface" value="com.test.server.IMyTestService" />
</bean>
</beans>

We have just completed our simple service and all the required configuration files. Let us now deploy them before writing a sample client.

Copy the classes IMyTestService and MyTestService under WEB-INF/classes. We would require spring-2.5.4.jar and commons-logging.jar, copy them under WEB-INF/lib. Copy web.xml, applicationContext.xml and remoting-servlet.xml under WEB-INF

Restart tomcat and we are done.

Let us write a simple client now.

1. Write applicationContextClient.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<bean id="myService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl" value="http://localhost:8080/test/myService" />
<property name="serviceInterface" value="com.test.server.IMyTestService" />
</bean>
</beans>

2. Write java class to test the service.

package com.test.client;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.test.server.IMyTestService;

public class MyTestClient {
public static void main (String [] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("config/applicationContextClient.xml");
IMyTestService testService = (IMyTestService) context.getBean("myService");
int result = testService.authenticateUser("Pranav", "123123");
System.out.println("testService.authenticateUser - " + result);
}
}

Isn't it so simple….

Wednesday, April 8, 2009

Struts 2, Apache Tiles 2 - View Preparer - Dynamic Menu Creation

Let us develop as page using Struts 2, Apache Tile and Spring. This page has the following tiles – Header, Menu, Body and Footer. Let us have our Menu which is dynamic in nature which gets all menu items from database. So, we will use View Preparer to prepare the Menu before rendering.

Our tile definition file (tiles.xml) would look like

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_2_0.dtd">

<tiles-definitions>
<definition name="test.homepage" template="/htdocs/layouts/homepage.jsp">
<put-attribute name="logo" value="/htdocs/tiles/logo.jsp" />
<put-attribute name="menu" value="test.menu" />
<put-attribute name="body" value="/htdocs/tiles/body.jsp" />
<put-attribute name="footer" value="/htdocs/tiles/footer.jsp" />
</definition>

<definition name="test.menu" preparer="menuPreparer" template="/htdocs/tiles/menu.jsp" />

</tiles-definitions>

I have associated a preparer “menuPreparer’ to the tile definition for the menu. “menuPreparer” is the bean defined in applicationContext.xml below. Our bean definition file (applicationContext.xml) looks like

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:annotation-config />

<bean id="menuService" class="test.MenuService"
/>

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/tiles.xml</value>
</list>
</property>
<property name="preparerFactoryClass" value="org.springframework.web.servlet.view.tiles2.SpringBeanPreparerFactory"/>
</bean>

<bean id="menuPreparer" class="test.MenuViewPreparer"
/>
</beans>

Here, we have defined a bean test.MenuViewPreparer whose id “menuPreparer” is defined in tiles.xm above.

test.MenuViewPreparer implements ViewPreparer interface whose execute method is called before tile is rendered. In this execute method we will get the list of all the menus to be displayed. We will use the service test.MenuService to get the list of all menu items. This service can get all the menus from database. This service (bean) is defined in the applicationContext above.

Now we have to inject menuService to test.MenuViewPreparer. So, how do I do this?

I will use annotations to configure Spring’s dependency injection. So, I have <context:annotation-config /> in my applicationContext.xml.

This is my test.MenuViewPreparer

package test;

import java.util.List;

import org.apache.tiles.Attribute;
import org.apache.tiles.AttributeContext;
import org.apache.tiles.context.TilesRequestContext;
import org.apache.tiles.preparer.PreparerException;
import org.apache.tiles.preparer.ViewPreparer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MenuViewPreparer implements ViewPreparer {
@Autowired
private IMenuService menuService;

private List<Menu> menuList;

public IMenuService getMenuService() {
return menuService;
}

public void setMenuService(IMenuService menuService) {
this.menuService = menuService;
}

public List<Menu> getMenuList() {
return menuList;
}

public void setMenuList(List<Menu> menuList) {
this.menuList = menuList;
}

public void execute(TilesRequestContext tilesContext,
AttributeContext attributeContext) throws PreparerException {
menuList = menuService.getMenuList();
attributeContext.putAttribute("menuItems", new Attribute(menuList));
System.out.println(menuList);

}

}

Here I have used @Service and @Autowired. @Autowired is used to autowire the dependency of the MenuViewPreparer on the IMenuService. Here is the IMenuService interface.

package test;

import java.util.List;

public interface IMenuService {

List<Menu> getMenuList();

}

Here is test.Menu

package test;

public class Menu {
private String menuName;

private String menuURL;

public Menu(String menuName, String menuURL) {
super();
this.menuName = menuName;
this.menuURL = menuURL;
}

public String getMenuName() {
return menuName;
}

public void setMenuName(String menuName) {
this.menuName = menuName;
}

public String getMenuURL() {
return menuURL;
}

public void setMenuURL(String menuURL) {
this.menuURL = menuURL;
}
}

At last let us have a look at homepage.jsp and menu.jsp. This is my homepage.jsp

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<html>
<head>
<title>Test</title>
</head>

<body>
<div id="main">
<tiles:insertAttribute name="logo"/>
<tiles:insertAttribute name="menu"/>
<tiles:insertAttribute name="body"/>
<tiles:insertAttribute name="footer"/>
</div>
</body>
</html>

menu.jsp looks like –

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>

<tiles:importAttribute name="menuItems" />
<c:forEach var="item" items="${menuItems}">
<a href="${item.menuURL}"> ${item.menuName}</a>

</c:forEach>

<br><br>

Here I have used JSTL to display the menu items. Test.MenuViewPreparer’s execute method puts the attribute menuItems in attributeContext. So, on JSP I get this attribute and display the menu (name and URL) on the page.

Libraries used –

Apart from Struts2-core, xwork, tiles-api, tiles-core these jars are required

spring.jar (Spring 2.5 and above)
JSTL jars - standard.jar, jstl.jar

Note – You do not have to initialize “StrutsTilesListener” in your web.xm. As we are using Spring to load the tile definitions.

Some interesting Apache Rewrite rules

Example 1
I have a directory having installers for different languages. User can download installer for any language. If user requests an installer for a language that is not supported, then the “en” installer has to be provided.

These installers are stored as -

<DOCUMENT_ROOT>/installers/<TWO_CHARS_LOCALE>/installer.exe like,
<DOCUMENT_ROOT>/installers/en/installer.exe,
<DOCUMENT_ROOT>/installers/de/installer.exe etc.

User can request an installer using the link http://www.mysite.com/installers/en/installer.exe

How do we achieve this language fallback – if an installer for a language is not available, provide English installer?

One way of achieving it is to write a 404 handler.

But how about writing a simple rewrite rule?

RewriteEngine On
RewriteLog "logs/rewrite.log"
RewriteLogLevel 9
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f [NC]
RewriteRule (^/installers)/../(installer.exe)$ $1/en/$2 [L]

More examples to follow soon ..

Tuesday, April 7, 2009

Spring Quartz

Let us now integrate Spring Quartz with the previous example. Let us check if there are any new match detail available every 1 hour. If we get new match detail; add it to the previously stored match details, create the new JSON feed and publish it.

Integrating Quartz Scheduler with Spring is very easy. You just have to add required beans into your bean definition file (applicationContext.xml). So, in our case it would look like –

< beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >

< ! - -
This will call generateFeed method on feedGenerator bean defined below. To make jobs resulting from MethodInvokingJobDetailFactoryBean as non-concurrent, I have set the concurrent flag to false.
- - >

< bean id="jsonFeedJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
< property name="targetObject" ref="feedGenerator" />
< property name="targetMethod" value="generateFeed" />
< property name="concurrent" value="false" />
< /bean >

< ! - -
matchService bean is defined that has business logic to get the recent match detail. May be getting it from database or some other third party feed.
- - >
< bean id="matchService" class="test.SomeMatchService" / >

< ! - -
feedGenerator has the logic of generating JSON feed and has the method generateFeed(). SomeMatchService is injected into it
- - >
< bean id="feedGenerator" class="test.FeedGenerator"
p:matchService -ref="matchService"
/ >


< ! - -
Creates SimpleTriggerBean with start delay of 50 second and that repeats every 1 hour
- - >
< bean id="jsonFeedTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean" >
< property name="jobDetail" ref="jsonFeedJob" / >
< property name="startDelay" value="50000" / >
< property name="repeatInterval" value="3600000" / >
< / bean >

< ! - -
Finally adding the SimpleTriggerBean to SchedulerFactoryBean
- - >
< bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
< property name="triggers" >
< list >
< ref bean="jsonFeedTrigger" / >
< /list >
< /property >
< /bean >
< /beans >

Now add the following class to start your job

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class JsonJobHandler {
public static void main (String [] args) {
String[] paths = { "file:applicationContext.xml" };
ApplicationContext ctx = new ClassPathXmlApplicationContext(paths);
}
}

Instead of generating feed every 1 hour, if we want to generate feed every morning at 5:30 AM we can use CronTriggerBean instead of SimpleTriggerBean

<bean id="jsonFeedTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jsonFeedJob" />
<property name="cronExpression" value="0 30 5 * * ?" />
</bean>

Isn’t So Easy?

JSON Feed using XStream

Let us write a sample code to generate JSON feed for some matches that can be shared across websites. JSON format looks like as mentioned below – It is an array of several matches played. We have to fetch the match detail (recent match detail) every after ‘n’ minute and create an array having all old match details and the recent match detail. Match detail has match id, match play time, prize amount given and players.

{
"matchDetails": [
{
"matchId": 101,
"time": 937858019,
"prizeAmount": 10000,
"players": [
"John",
"Jean",
"Mike"
]
},
{
"matchId": 102,
"time": 937858019,
"prizeAmount": 12000,
"players": [
"Vivian",
"Angela",
"Andrew"
]
}
]
}


I have used Spring Quartz for scheduling job and XStream to convert java object into JSON.

XStream is very powerful library that converts Java Object to XML / JSON and back again.

So, let us start with writing java class MatchDetail that holds match details.

package test;

import java.util.List;

public class MatchDetail {
private int matchId;

private long time;

private int prizeAmount;

private List players;

public int getMatchId() {
return matchId;
}

public void setMatchId(int matchId) {
this.matchId = matchId;
}

public long getTime() {
return time;
}

public void setTime(long time) {
this.time = time;
}

public int getPrizeAmount() {
return prizeAmount;
}

public void setPrizeAmount(int prizeAmount) {
this.prizeAmount = prizeAmount;
}

public List getPlayers() {
return players;
}

public void setPlayers(List players) {
this.players = players;
}

}

This class generates the JSON Feed –

package test;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;

public class FeedGenerator {
IMatchService matchService;

public IMatchService getMatchService() {
return matchService;
}

public void setMatchService(IMatchService matchService) {
this.matchService = matchService;
}

public void generateFeed() throws Exception {
// Get the recent match details
MatchDetail detail = getRecentMatchDetail();

// Get the stored feed
List details = getMatchDetails();

// Add the current match detail to the list
details.add(detail);

// Save it back to the disk
storeMatchDetails(details);

// I have to create JSON – format as shown above, so I write the
// following to store match details as JSON.
saveMatchDetailsAsJSON(details);

}

private MatchDetail getRecentMatchDetail() {
return matchService.getRecentMatchDetail();
}

private List getMatchDetails() throws Exception {
XStream xstream = new XStream();

File file = new File("feed.xml");
if (!file.exists()) {
return null;
}

FileReader fr = new FileReader(file);
ObjectInputStream in = xstream.createObjectInputStream(fr);
List wmds = (ArrayList) in.readObject();
in.close();
fr.close();
return wmds;
}

private void storeMatchDetails(List whds) throws Exception {
XStream xstream = new XStream();

File file = new File("feed.xml");
FileWriter fw = new FileWriter(file);
ObjectOutputStream out = xstream.createObjectOutputStream(fw);

out.writeObject(whds);
out.close();
fw.close();
}

private void saveMatchDetailsAsJSON(List whds)
throws Exception {
XStream xstream = new XStream(new JsonHierarchicalStreamDriver() {
public HierarchicalStreamWriter createWriter(Writer writer) {
return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE);
}
});

xstream.setMode(XStream.NO_REFERENCES);
xstream.alias("matchDetails", List.class);

File file = new File("matchDetails.js");
FileWriter fw = new FileWriter(file);
ObjectOutputStream out = xstream.createObjectOutputStream(fw);

out.writeObject(whds);
out.close();
fw.close();
}

}

I have to use intermediate feed.xml to store match details. I could have used matchDetails.js but after using “DROP_ROOT_MODE”, alias etc (as I need the JSON format as mentioned above) I was not able to convert JSON back to java object.

Libraries Used –

xstream-1.3.1.jar
xpp3_min-1.1.4c.jar
jettison-1.0.1.jar

Next we will discuss Spring Quartz