Archive for March, 2011



02
Mar
11

Spring Framework based RESTful Web Service

This page shows you how to quickly and easily create a read-only Spring based RESTful Web Service / Application.

To keep things simple I will not be covering the “front-end” view technologies. Please see one of my other pages on how to get Spring MVC, JSF, extJS or even standard Servlets/JSP’s running with the application discussed here.

You can drop in any “view technologies” once you have the below application running.

The application will retrieve a list of cities from a “mock” datamanager and display them in JSON format in a browser.

The page should take 10-15 minutes to implement.

Requirements

  • Basic understanding of Spring
  • Basic understanding of REST
  • Java 5
  • Maven 2

Procedure

Configure the project

Create a project using Maven archetype. Open up the command prompt and navigate to an empty directory.

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

groupId: com.test
artifactId: springRestWeb

Answer the rest of the questions with defaults “Just hit the enter key”,

Cd to the project base folder.

cd springRestWeb

Since this is a web project maven does not create the java source foldder.

Create missing directory
mkdir -p src/main/java/com/test

Modify the project configuration file

Open up the pom.xml file and make sure it looks like the one below. In most cases you can just copy and paste the one below into your own.

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>com.test</groupId>
  <artifactId>springRestWeb</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>springRestWeb</name>
  <url>http://maven.apache.org</url>
    <repositories>
        <repository>
           <id>maven-restlet</id>
           <name>Public online Restlet repository</name>
           <url>http://maven.restlet.org</url>
        </repository>
    </repositories>
 
  <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>

    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20090211</version>
    </dependency>        
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2</version>
    </dependency>
        
    <dependency>
       <groupId>org.restlet.jee</groupId>
       <artifactId>org.restlet</artifactId>
       <version>2.0.5</version>
    </dependency>
    <dependency>
       <groupId>org.restlet.jse</groupId>
       <artifactId>org.restlet.ext.simple</artifactId>
       <version>2.0.5</version>
    </dependency>
    <dependency>
       <groupId>org.restlet.jee</groupId>
       <artifactId>org.restlet.ext.spring</artifactId>
       <version>2.0.5</version>
    </dependency>
    
    <dependency>
       <groupId>org.restlet.jee</groupId>
       <artifactId>org.restlet.ext.servlet</artifactId>
       <version>2.0.5</version>
    </dependency>
    
    <!-- spring framework -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>3.0.1.RELEASE</version>
    </dependency>
        
        <dependency>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-servlet_2.5_spec</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>
  </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
				<version>7.0.0.v20091005</version>
                <configuration>
                    <scanIntervalSeconds>2</scanIntervalSeconds>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Web Application Configuration

The application uses the “org.restlet.ext.spring.SpringServerServlet” to respond to HTTP Requests. This servlet responds to URL’s that begin with “/app/*” and delegates the handling of the requests to the Resources based on the routing logic specified in the ApplicationContext.xml.

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
  <display-name>Archetype Created Web Application</display-name>

<!-- The following is  used to initialize the spring framework when the web app starts. -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 
     <servlet>
        <servlet-name>stateApplication</servlet-name>
        <servlet-class>
             org.restlet.ext.spring.SpringServerServlet
        </servlet-class>
        <init-param>
			<param-name>org.restlet.application</param-name>
			<param-value>stateApplication</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>stateApplication</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
 
 
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
 
</web-app>

Spring Configuration

The stateApplication uses a router that further checks the URL and calls the proper “Resource” based on the mappings. The routing logic consists of a “url pattern” that will cause the specified “Resource” to be called upon to respond to the request.

Each map entry uses a “SpringFinder” to create an instance of the Resource. The instance of the resource can have any number of beans injected into it (based on spring configuration).

src/main/webapp/WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        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">

	<bean id="stateDataManager" class="com.test.StateDataManager"/>

	<bean id="stateApplication" class="org.restlet.Application" scope="singleton">
		<property name="root">
			<bean class="org.restlet.ext.spring.SpringRouter">
				<property name="attachments">
					<map>
						<entry key="/state/{name}">
							<bean class="org.restlet.ext.spring.SpringFinder">
								<lookup-method name="create" bean="stateResource" />
							</bean>
						</entry>
					</map>
				</property>
			</bean>
		</property>
	</bean>

	<bean id="stateResource" class="com.test.StateResource" scope="prototype">
		<property name="stateDataManager" ref="stateDataManager"/>
    </bean>
		
</beans>

Create the Resource

The “Resource” is called upon to respond to requests. The “stateDataManager” member will be injected using the spring configuration seen above.

src/main/java/com/test/StateResource.java

package com.test;

import org.json.JSONArray;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ResourceException;
import org.restlet.resource.ServerResource;
import org.springframework.beans.factory.annotation.Required;

public class StateResource extends ServerResource {
	// the stateDataManager is injected using Spring
	private StateDataManager stateDataManager;
	
	// ServerResources are thread safe (spring "prototype")
	private String state;
	
    public StateResource() {
        super();
    }
    @Override
    protected void doInit() throws ResourceException {
    	super.doInit();
    	state = (String)getRequestAttributes().get("name");
    }
    @Override
    protected Representation get() throws ResourceException {
    	
        JSONArray jsonArray = new JSONArray(stateDataManager.getCities(state));
        StringBuffer out = new StringBuffer();
        String cb = getQuery().getFirstValue("callback");
        out.append(cb + "(");
        out.append("{ \"totalCount\" : \"" + jsonArray.length() + "\",");
        out.append("\"records\" : ");
        out.append(jsonArray.toString());
        out.append("}");
        out.append(");");
        
        return new StringRepresentation(out.toString());
    }
    @Required
	public void setStateDataManager(StateDataManager stateDataManager) {
		this.stateDataManager = stateDataManager;
	}
	public StateDataManager getStateDataManager() {
		return stateDataManager;
	}
}

Data Manager

This is the dummy data manager. In your situation you would replace the hard coding with a connection to a real database. I have many examples where Spring is used to connect to a mySQL database. Please search for “SimpleJDBC” on the top right corner.

src/main/java/com/test/StateDataManager.java

package com.test;

import java.util.ArrayList;
import java.util.List;

/**
 * This is a test data manager that returns dummy data.
 */
public class StateDataManager {
	public List<String> getCities(String state) {
		// return the list of cities given a state		
		List<String> cityList = new ArrayList<String>();		
		if("TX".equals(state)) {
			cityList.add("Houston");
			cityList.add("San Antonio");
			cityList.add("Dallas");
			cityList.add("Austin");
			cityList.add("El Paso");
			cityList.add("Fort Worth");			
		} else if("NY".equals(state)) {
			cityList.add("New York City");
			cityList.add("Buffalo");
			cityList.add("Rochester");
			cityList.add("Yonkers");
			cityList.add("Syracuse");
		} else if("CA".equals(state)) {
			cityList.add("Los Angeles");
			cityList.add("San Diego");
			cityList.add("San Jose");
			cityList.add("San Francisco");
			cityList.add("Fresno");
		} else {
			// don't add any cities
		}
		return cityList;
	}
}

Test the Application

mvn clean compile jetty:run

Use your browser to navigate to the following URLs:
http://localhost:8080/app/state/CA
http://localhost:8080/app/state/NY
http://localhost:8080/app/state/TX

You should see something like this in your browser:

null({ "totalCount" : "5","records" : ["Los Angeles","San Diego","San Jose","San Francisco","Fresno"]});

The above format is designed to work with the extJS “Ext.data.ScriptTagProxy”. But you can use any number of view technologies to parse and display the data.

Creating a WAR file

If you want to run the application in another environment you can create a war file and drop it into a server of your choice. This method will also package up any runtime dependencies and put them in the /WEB-INF/lib folder of the WAR file.

mvn clean compile package

The war file should appear in the following location…

springRestWeb/target/springRestWeb-1.0-SNAPSHOT.war




Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 54 other followers

March 2011
S M T W T F S
« Feb   Apr »
 12345
6789101112
13141516171819
20212223242526
2728293031  

Blog Stats

  • 528,871 hits

Follow

Get every new post delivered to your Inbox.

Join 54 other followers