Showing posts with label ws. Show all posts
Showing posts with label ws. Show all posts

Friday, October 31, 2008

XML-RPC vs. SOAP vs. REST thoughts and Spring Beans

Recently I am working on the remote API side of my project, and the protocol that we chose is XML-RPC. Somewhere in two blogs returned by Google search suggested that REST > XML-RPC > SOAP. SOAP can only be considered for its wide enterprise adoption and protocol stack (with security management, transaction control, etc.) and therefore it is complicated. Yes, the person created XML-RPC wasn't an expert in XML but it's neat.

Why not REST then? I used to work on design and technical issues of Mule ESB and I was exposed to REST of its sweetness (the simplicity, the CRUD stuff) before more people started to talk about it (now they talk about Cloud). I learned that SOAP is for SOA (Service Oriented Architecture) and REST is for ROA (Resource Oriented Architecture). They are two different paradigms, and the purpose and design of these layers are therefore different, e.g. you don't carry a service/RPC mindset when you create a REST API.

So, no REST for now as IMO it requires more effort to design a proper API. Never I'll create SOAP due to its complexity.

What about JSON-RPC? I had a short discussion regarding this with @ditesh at #myoss@irc.freenode.net. I love JSON for its interoperability and speed (vs. XML parsing). But the available implementations turned me off pretty quickly, most of them stuck in year 2005~2007. For a publicly available remote API, a well-adopted and "mature" protocol is a much better choice.

What about the Spring beans stuff mentioned in the topic? I found the Apache XML-RPC server can't be integrated with Spring naturally (it has but not enough of bean property methods) and the API is quite ugly, e.g. XmlRpcSystemImpl.addSystemHandler() takes a PropertyHandlerMapping, why not XmlRpcListableHandlerMapping?

I took a similar approach suggested by Tomas Salfischberger in his blog and here they are:
  • An implementation of Spring's AbstractController.
  • An extension of PropertyHandlerMapping, this bean is used by the previous.

public class MyXmlRpcController extends AbstractController {
private XmlRpcServletServer server = new XmlRpcServletServer();
private boolean introspectionEnabled = false;

@Override
protected void initServletContext(ServletContext servletContext) {
super.initServletContext(servletContext);
if (this.introspectionEnabled) {
try {
XmlRpcSystemImpl.addSystemHandler((PropertyHandlerMapping) this.server.getHandlerMapping());
} catch (Exception e) {
}
}
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
this.server.execute(request, response);
return null;
}
@Required
public void setHandlerMapping(XmlRpcHandlerMapping serverHandlerMapping) {
this.server.setHandlerMapping(serverHandlerMapping);
}
public void setIntrospectionEnabled(boolean introspectionEnabled) {
this.introspectionEnabled = introspectionEnabled;
}
}

public class MyXmlRpcHandlerMapping extends PropertyHandlerMapping {
private Map serviceMap;

public void init() throws XmlRpcException {
this.load(Thread.currentThread().getContextClassLoader(), serviceMap);
}
@Required
public void setServicesMapping(Map serviceMap) {
this.serviceMap = serviceMap;
}
}


<bean id="xmlRpcHandlerMapping" class="my.MyXmlRpcHandlerMapping" init-method="init">
<property name="servicesMapping">
<map>
<entry key="membershipService" value="my.service.XmlRpcMembershipService"></entry>
</map>
</property>
</bean>
<bean id="urlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>/xmlrpc=xmlRpcController</value>
</property>
</bean>
<bean id="xmlRpcController" class="my.MyXmlRpcController">
<property name="handlerMapping" ref="xmlRpcHandlerMapping"></property>
<property name="introspectionEnabled" value="true"></property>
</bean>


- yc

Sunday, December 23, 2007

Quick Dive into CXF

REST
REST is no longer a new term, you are probably falling too far behind if you haven't yet heard of it. Daniel Diephouse, the creator of XFire (who is also now a Software Architect of MuleSource) wrote an interesting article about REST and SOA not too long ago, he sees REST as the next big trend in SOA.

If you are into this and if you are a Java guy, you may also be aware of the JSR 311 - The JavaTM API for RESTful Web Services and the Jersey (implementation). You would have also been told that Apache Axis 2.0 and Apache CXF (Celtic XFire) supports REST.

The rest of the blog entry, I will be sharing about how I toyed around with CXF, after taking a quick glance at it and learned how easy using it to write a Web-/REST- service.

Get a POM
<?xml version="1.0" encoding="UTF-8"?>
<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>sandbox</groupId>
<artifactId>apache-cxf</artifactId>
<version>2007.12.18</version>

<properties>
<cxf.version>2.1-incubator-SNAPSHOT</cxf.version>
<spring.version>2.0.6</spring.version>
</properties>

<repositories>
<repository>
<id>apache.incubating.releases</id>
<name>Apache Incubating Release Distribution Repository</name>
<url>http://people.apache.org/repo/m2-incubating-repository</url>
</repository>
<repository>
<id>apache.snapshot.releases</id>
<name>Apache Snapshot Release Distribution Repository</name>
<url>http://people.apache.org/repo/m2-snapshot-repository</url>
</repository>
</repositories>

<dependencies>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.1</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>

<!-- Depending on your requirements you may need more or less modules from cxf -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-core</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-simple</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- REST!! -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-aegis</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-local</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-jms</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-management</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-common-utilities</artifactId>
<version>${cxf.version}</version>
</dependency>
<!--dependency>
<groupId>jersey</groupId>
<artifactId>jersey</artifactId>
<version>0.4-ea</version>
</dependency-->
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>

</project>


Write the Code
Once you have built the project into your favourite IDE, start writing the code:
  1. A service interface

  2. A service implementation

  3. A Java main class


package sandbox.cxf.api;
import javax.jws.WebParam;
import javax.jws.WebService;

import sandbox.cxf.resources.Author;
import sandbox.cxf.resources.Message;

@WebService(name = "sampleService")
public interface SampleService {

public Message getEcho(@WebParam(name="input") String string);
...
}

package sandbox.cxf.services;
import javax.jws.WebService;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.UriParam;
import javax.ws.rs.UriTemplate;

import sandbox.cxf.api.SampleService;
import sandbox.cxf.resources.Message;

@UriTemplate("/sampleService")
@WebService(endpointInterface="sandbox.cxf.api.SampleService", serviceName="defaultSampleService")
public class DefaultSampleService implements SampleService{

@HttpMethod("GET")
@UriTemplate("/echo/{input}")
public Message getEcho(@UriParam("input") String string){

System.out.println("Echoing message of " + string);

Message message = new Message();
message.setBody(string);

return message;
}
...
}


No *.properties or *.xml crap, this is how quickly you can configure and boot a SOAP and REST service:

SampleService sampleService = new DefaultSampleService();

JaxWsServerFactoryBean wsServerfactoryBean = new JaxWsServerFactoryBean();
wsServerfactoryBean.setServiceClass(SampleService.class);
wsServerfactoryBean.setAddress("http://localhost:8081/sampleService");
wsServerfactoryBean.setServiceBean(sampleService);
wsServerfactoryBean.create();

JAXRSServerFactoryBean restServerFactoryBean = new JAXRSServerFactoryBean();
restServerFactoryBean.setResourceClasses(Message.class);
restServerFactoryBean.setBindingId(JAXRSBindingFactory.JAXRS_BINDING_ID);
restServerFactoryBean.setServiceBeans(sampleService);
restServerFactoryBean.setAddress("http://localhost:8082/");
restServerFactoryBean.create();


Add logging interceptors if you want some logging and debugging:

wsServerfactoryBean.getInInterceptors().add(new LoggingInInterceptor());
wsServerfactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());


Try Now
Once you have done the above, try to access the services via browser, e.g.

REST: http://localhost:8082/sampleService/echo/foobar
<message>  <body>foobar</body>
</message>


SOAP: http://localhost:8081/sampleService/getEcho?input=foobar
<soap:envelope soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:body>
<ns1:getechoresponse ns1="http://api.cxf.sandbox/">
<ns2:message ns2="http://api.cxf.sandbox/">
<body>foobar</body>
</ns2:Message>
</ns1:getEchoResponse>
</soap:Body>
</soap:Envelope>


Isn't that quick? ;-)

- yc