2007/11/21
21 Nov, 2007

Invoking a Service in a REST Manner

  • Deepal Jayasingha
  • - WSO2

Introduction

 Axis2 has a very convenient API for invoking or interacting with a remote service. The API consist of two sub APIs called ServiceClient and OperationClient. ServiceClient is intended for novice/regular users who only need to send and receive some XML. Whereas OperationClient is intended for advanced users who want to do additional tweaking with the message. Irrespective of the client API we use to invoke the service with, if the default configuration is used, then the wire format of the message is SOAP/XML.

However when using the REST style of service invocation then the wire message would not be a SOAP message, but rather a simple XML or Plain Old XML (POX) message.

Just to get a feel for how REST works, the easiest way is to start with a sample. So let's say we have a service called Calculator hosted in Axis2, and assume that it has a method called Add. Then the service class will look as follows.

public class Calculator {     
public int add(int arg0, int arg1) {
         return arg0 + arg1;
     }
 }

We can deploy the service using a single line as shown below. You'll need to add the .jar files from the Axis2 distribution to your classpath to compile and execute the  code samples in this  tutorial.

import org.apache.axis2.AxisFault;
import org.apache.axis2.engine.AxisServer;
public class QuickServer {
     public static void main(String[] args) throws AxisFault {
         new AxisServer().deployService(Calculator.class.getName());
     }
 }

Whn you run the QuickServer class, it deploys the service and starts the SimpleHTTPServer on port 6060. Now if you go to https://localhost:6060 you will see the following in the browser.

Browse out put

First let's try to invoke the service without writing a single line of code. In simple terms, try to invoke the service using a browser. Open a browser and type the following in the address bar.

https://localhost:6060/axis2/services/Calculator/add?arg0=10&arg1=20

You should see the following response in the browser, which you can see is the sum of the 2 arguments you passed as parameters in the URL.

<ns:addResponse>
   <return>30</return>
</ns:addResponse>

As you can see, you deployed the service and invoked it in a REST manner. So you can see that when a service is deployed in Axis2, it enables both SOAP and REST interfaces for it. Therefore, you can invoke the same service in a REST manner or a SOAP manner.

Now let's try to invoke the service as a SOAP service by writing a client. We are not going to use AXIOM for writing the client and hence our task would be simpler. The code for the client is given below. You might wonder why the code sets the port in the endpoint address to 6070, and not 6060 where the Axis2 server is running. In order to see what passes on the wire,

I have configured the client EPR to send the request via TCP monitor. You will need to start TCPMon listening on port 6070 and forwarding to 6060.

import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.rpc.client.RPCServiceClient;
import javax.xml.namespace.QName;
public class CalculatorClient {
     public static void main(String[] args) throws Exception {
         RPCServiceClient client = new RPCServiceClient();
         Options opts = new Options();
         opts.setTo(new EndpointReference("https://localhost:6070/axis2/services/Calculator/add"));
         opts.setAction("urn:add");
         client.setOptions(opts);


         Object[] values = client.invokeBlocking(new QName("http://ws.apache.org/axis2", "add"), new Object[]{10, 20},                 new Class[]{int.class});
         System.out.println(values[0]);
     }
    }

When you run the client, you will see the following in the TCP monitor.

Request Message

 POST /axis2/services/Calculator/add HTTP/1.1 Content-Type: text/xml;
charset=UTF-8 SOAPAction: "urn:add" User-Agent: Axis2 Host: 127.0.0.1
Transfer-Encoding: chunked 100
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="https://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<add xmlns="http://ws.apache.org/axis2">
<arg0 xmlns="">10</arg0>
<arg1 xmlns="">20</arg1>
</add>
</soapenv:Body>
</soapenv:Envelope>

Response Message

 HTTP/1.1 200 OK Date: Wed, 21 Nov 2007 15:26:38 GMT Server: Simple-Server/1.1


Transfer-Encoding: chunked Content-Type: text/xml; charset=UTF-8 fc
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="https://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:addResponse xmlns:ns="http://ws.apache.org/axis2">
<return>30</return>
</ns:addResponse>
</soapenv:Body>
</soapenv:Envelope>

You can clearly see that both the request and the response are SOAP messages.

Now let's try to send the request as a POX or a REST POST message. To do that you will need to add the following property in the client.

 opts.setProperty(Constants.Configuration.ENABLE_REST, Boolean.TRUE); 

After adding this property, run the client again. Then the TCPMon output will be as follows.

Request Message

 POST /axis2/services/Calculator/add HTTP/1.1 Content-Type: application/xml;
charset=UTF-8 SOAPAction: urn:add User-Agent: Axis2 Host: 127.0.0.1 Transfer-Encoding: chunked 5e
<add xmlns="http://ws.apache.org/axis2">
<arg0 xmlns="">10</arg0>
<arg1 xmlns="">20</arg1>
</add>

Response Message

 HTTP/1.1 200 OK Date: Wed, 21 Nov 2007 15:32:43 GMT Server: Simple-Server/1.1 
Transfer-Encoding: chunked Content-Type: application/xml; charset=UTF-8 5a
<ns:addResponse xmlns:ns="http://ws.apache.org/axis2">
<return>30</return>
</ns:addResponse>

As we can see, neither the request message nor the response message is SOAP. In both messages you should see only the payload. This is simply an example of invoking a service in a REST (POST) manner using the Axis2 client API.

Now let's try to invoke the service in a REST GET manner. But you need to remember that you cannot invoke every service using the GET manner. You can only do that if the request is going to follow the IRI pattern for its URI. To invoke the service in a REST GET manner, you will need to add one more property to the client code. That will be as follows.

 opts.setProperty(Constants.Configuration.HTTP_METHOD, Constants.Configuration.HTTP_METHOD_GET); 

Once you run the client with the above changes, yo should see the following output in TCPMon.

Request Message

 GET /axis2/services/Calculator/add?arg0=10&arg1=20 HTTP/1.1 Content-Type: application/x-www-form-urlencoded;
charset=UTF-8;action="urn:add";
SOAPAction: urn:add User-Agent: Axis2 Host: 127.0.0.1

Response Message

 HTTP/1.1 200 OK Date: Wed, 21 Nov 2007 15:41:48 GMT Server: Simple-Server/1.1 Transfer-Encoding: chunked Content-Type: application/xml;
charset=UTF-8 5a
<ns:addResponse xmlns:ns="http://ws.apache.org/axis2">
<return>30</return>
</ns:addResponse>

 

As you can see in the HTTP headers, the request is GET and all the parameters are included in the URL. In addition to that the response is a simple XML or POX messages. However there is one limitation when we use RPCServiceClient to invoke a Service in a REST GET manner. That is, if the service class is similar to the one shown below (where the method parameters are not arg0 , arg1 etc..), then we cannot use RPCServiceClient to invoke the service in a REST GET manner.

Note : RPCServiceClient is a very convenient API built using ServiceClient to send and receive Java beans . So we can use RPCServiceClient to invoke a service without worrying about Axiom.

 public class Calculator {
public int add(int a , int b){
return a + b;
}
}

If you have a service like the above, you can use ServiceClient to invoke the service if you don't want to generate  client code. In this particular case, you can write the service client as follows,

 public static void main(String[] args) throws AxisFault {
ServiceClient client = new ServiceClient();
Options opts = new Options();
opts.setTo(new EndpointReference("https://localhost:6070/axis2/services/Calculator/add"));
opts.setAction("urn:add");
opts.setProperty(Constants.Configuration.ENABLE_REST, Boolean.TRUE);
opts.setProperty(Constants.Configuration.HTTP_METHOD, Constants.Configuration.HTTP_METHOD_GET);
client.setOptions(opts);
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("http://ws.apache.org/axis2", "ns");
OMElement method = fac.createOMElement("add", omNs);
OMElement value = fac.createOMElement("a", omNs);
method.addChild(value);
value.setText("10");
OMElement value2 = fac.createOMElement("b", omNs);
method.addChild(value2);
value2.setText("10");
OMElement res = client.sendReceive(method);
System.out.println(res);
}

Summary

In this tutorial we discussed how to expose a simple Java class as a Web service. And then we demonstrated invoking that via ServiceClient, RPCServiceClient as well as using the browser. You can easily extend this sample and implement a somewhat complex REST service.

Author

Deepal Jayasinghe, Tech Lead, WSO2 Inc. deepal at wso2 dot com

 

About Author

  • Deepal Jayasingha
  • WSO2 Inc.