Thursday, 5 January 2012

Implementing RESTful web services using Spring In Java


Couple of weeks back my collegue Phil M implemented spring rest implementation in our project and i thought of sharing with everyone.

We can use Spring APIs and annotations to build RESTful web services seamlessly.

Other alternatives to implement RESTful web service is to use JSR 311(JAX-RS) and its implementation Jersey else we may also use Restlet framework.

The Jaxb2Marshaller offers a classesToBeBound property, which allows you to set an array of classes to be supported by the marshaller.

With the representational state transfer (REST) style architecture consists of client and server and requests and responses are built around the transfer of representations of resources. Resources are identified by global IDs that typically use a uniform resource identifier (URI). Client applications use HTTP
methods (such as GET, POST, PUT, or DELETE) to manipulate the resource or collection of resources.

For example, GET http://host/context/dept/12345 gets the particular department details with the ID 12345. The response representation could be XML or it could be a JSP/HTML page.


Annotations such as @RequestMapping and @PathVariable, are used to support resource identification and URI mappings.


Please Note That I used Spring 3.0.4 Release.



Please find one example below


Server Side


demo.domain.address.AddressSearch.java

@XmlRootElement(name = "addresses")
public class AddressSearch implements Serializable {

private List<Address> addresses;
private String errorMessage;
private boolean validAddress;

...implement get set methods


demo.domain.address.Address.java

@XmlRootElement(name = "address")
public class Address implements Serializable {

private String address1;
private String address2;
private String city;
private String county;
private String postalCode;
ersion="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>

<wls:session-descriptorostalCode, @PathVariable String address1) throws Exception
{
AddressSearch addressSearch = addressService.getAddress(address1, postalCode);
return new ModelAndView(XML_VIEW_NAME, "addresses", addressSearch);
}

.........
}


AddressServiceImpl.java

@Service
public class AddressServiceImpl implements IQASService {

public AddressSearch getAddress(String houseNameOrNumber, String postalCode) {
@SuppressWarnings("rawtypes")
List addresses = new ArrayList();
AddressSearch as = new AddressSearch();

try {
addresses = ... your logic left to you how u get the data back maybe bring it from database or user another webservice etc...
} catch (Exception e) {
}

return as; // please note we have some internal logic to populate the list into a business object.
}
.....
}


web.xml

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
ramework.org/senera/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>qas</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>


weblogic.xml

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>

<wls:session-descriptor>
<wls:persistent-store-type>replicated_if_clustered</wls:persistent-store-type>
</wls:session-descriptor>

</wls:weblogic-web-app>



demo-service.xml

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

<bean id="addressService" class="demo.service.impl.AddressServiceImpl" />

<context:component-scan base-package="demo.service" />
</beans>


demo-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:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- Register the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter -->
<mvc:annotation-driven />

<!-- Component scan for controller classes -->
<context:component-scan base-package="demo.controller" />


<!-- Defines the marshaller/unmarshaller that uses JAXB 2 to do the object XML mapping (OXM) -->
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>demo.domain.address.AddressSearch</value>
</list>
</property>
</bean>

<!-- Defines an XML representation view that utilizes the Jaxb2Mashaller
//XML_VIEW_NAME in controller equals address, which is the view name defined in demo-servlet.xml
-->
<bean id="addresses" class="org.springframework.web.servlet.view.xml.MarshallingView">
<constructor-arg ref="jaxbMarshaller" />
</bean>

<bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver" />


<!--
REST services can produce different representations according to the request.
Spring 3 introduces a new view resolver called ContentNegotiatingViewResolver.
It can switch view resolvers according to request content type (the Accept property in the request header) or URI suffix.
-->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="html" value="text/html"/>
</map>
</property>
<property name="viewResolvers">
<list>
<!-- Defines a view resolver using the bean name that the user specifies

The definition shows support for handling two request content types: application/xml and text/html.
The code also defines two view resolvers: one BeanNameViewResolver to handle application/xml
and one UrlBasedViewResolver to handle text/html.

When we enter http://<host>:<port>/<appcontext>/addressTEST/getAddress/{postalCode}/{address1} in the browser it requests text/html
for the employees. Then UrlBasedViewResolver will take effect and Spring will pick /WEB-INF/jsp/addresses.jsp
as its view.
When you add the request header Accept:application/xml and invoke the request, the BeanNameViewResolver will take effect.

Per the code in AddressSearchController.java getAddressPcHouseNoName() method, it will use a view named addresses to represent,
which is the JAXB 2 marshaller view that was defined.

-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</list>
</property>
</bean>

</beans>


Please note that all the code described above can be packaged into a seperate war file and make it available in the client side


JUNIT Client Side :-


Now below Junit Class will help us to do some Unit Testing of the Controller.

TestAddressSearchController.java

public class TestAddressSearchController extends TestBaseController {

@Autowired
private RestTemplate restTemplate;

private static final String BASE_URL = "http://localhost:7001/addressTEST";

@Override
public void setUp() throws Exception {
super.setUp();
}

@Test
public void testGetAddressPcHouseNoName() {
String url = BASE_URL + "/getAddress/{postalCode}/{address1}";

postalCode = "NE28 9TA";
address1 = "165";
A//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.2/weblogic-web-app.xsd">
<wls:context-root>addressTEST</wls:context-root>

<wls:session-descriptor8String, Class, String...)
HEAD headForHeaders(String, String...)
OPTIONS optionsForAllow(String, String...)
POST postForLocation(String, Object, String...)
PUT put(String, Object, String...)

So to recap getForObject() will perform a GET, convert the HTTP response into an object type of your choice, and returns that object.
*/
AddressSearch as = restTemplate.getForObject(url,AddressSearch.class,mapParams);

assertTrue(as.getAddresses().size() == 1);
}
...
}


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath:spring/demo-service.xml",
"classpath:spring/demo-servlet.xml",
"classpath:spring/demo-test-rest.xml"})

public class TestBaseController {

protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected MockHttpSession session;

protected final static String GET = "GET";
protected final static String POST = "POST";

@Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.session = new MockHttpSession();
}

@After
public void tearDown() throws Exception {

}
}


demo-test-rest.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:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<!-- Rest Template -->

<!--

The RestTemplate is the central Spring class for client-side HTTP access.
RestTemplate is thread-safe once constructed, and that you can use callbacks to customize its operations.

-->

<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<property name="messageConverters" ref="messageConverter" />
</bean>

<!--
HttpMessageConverters Objects passed to and returned from the methods getForObject(), postForLocation(), and put() and are converted to HTTP requests and from HTTP responses by HttpMessageConverters.
-->

<bean id="messageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
<property name="marshaller" ref="jaxbMarshaller" />
<property name="unmarshaller" ref="jaxbMarshaller" />
</bean>


</beans>


Hope this example helps.




References
http://blog.springsource.org/2009/03/27/rest-in-spring-3-resttemplate/
http://www.ibm.com/developerworks/web/library/wa-spring3webserv/
http://static.springsource.org/spring-ws/site/reference/html/oxm.html
http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch18s02.html
http://en.wikipedia.org/wiki/Representational_State_Transfer

2 comments:

Anonymous said...

Hey There. I found your weblog using msn. That is an extremely well written article.
I'll be sure to bookmark it and come back to learn more of your useful info. Thanks for the post. I will definitely return.

my web blog ... 激安オークリー

Anonymous said...

Hi there! Do you know if they make any plugins to help with Search Engine Optimization?
I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good
gains. If you know of any please share. Thanks!

my homepage - サングラス オークリー