Sunday, December 1, 2013

Creating a Web Service from Apache Axis2

Apache Axis2 is a SOAP engine or a Web Service engine. It provides simple ways to create web services and / or web service clients. Same web service can be invoked using a SOAP message or a REST call. Axis2 uses Axiom as the method of data transferring. So from all the different ways you can create a web service using Axis2, using Axiom is the most flexible way. But it may sometimes be bit difficult and time consuming compared to some other methods like using POJO (Plain Old Java Objects) or ADB (Axis2 Data Binding). Here I'm going explain how to create a simple web service using Axiom.
First of all you have to download Axis2 binary distribution and the Axis2 WAR distribution. You can find it from here.
http://axis.apache.org/axis2/java/core/download.cgi

Then you have to install a java servlet engine like Apache Tomcat7. Here I'm going to use a Eclipse java EE version (Eclipse Indigo will be fine) as the IDE. A basic knowledge about the XML will be needed to proceed with writing a web service from Axiom. After having all these pre-requisites, now you are ready to start writing your web service.

Extract the WAR distribution zip file and copy it to the webapps folder in the tomcat7. (if you are using ubuntu your path will be "/var/lib/tomcat7/webapps" and you have to copy it with a sudo cp.) Then restart the server. This will create a folder named axis2 in the webapps folder.

Here I'm going to write a simple order processing system.

First create a java project in the Eclipse. Then in the src directory create a package named service. Inside that create a java file named OrderProcessor. Then right click on the project and go to Properties > Java Build Path > Libraries. Then click Add External JARs and add all the jars in the lib folder of the Axis2 binary distribution.
My web service has 2 methods. One is to create the order and the other is to get the details of the order later. You can add more methods to the web service. In this example I'm using just a Hash Map to store the data. Of course you have to store the data in a database if you are actually creating such a web service. So OrderProcessor class has 3 private variables to store the data.

      private HashMap<String, Double> itemPrice = new HashMap<>();
      private HashMap<String, HashMap<String, Integer>> order = new HashMap<>();
      private int ordercount = 0;

First method will be like this.

 public OMElement createOrder(OMElement element) {
element.build();
element.detach();

HashMap<String, Integer> orderItemTemp = new HashMap<>();
OMElement orderItem = (OMElement) element.getFirstElement();
OMElement itemElement = (OMElement) orderItem.getFirstOMChild();
OMElement quantityElement = (OMElement) itemElement.getNextOMSibling();
orderItemTemp.put(itemElement.getText(),
Integer.parseInt(quantityElement.getText()));

while ((orderItem = (OMElement) orderItem.getNextOMSibling()) != null) {
itemElement = (OMElement) orderItem.getFirstOMChild();
quantityElement = (OMElement) itemElement.getNextOMSibling();
orderItemTemp.put(itemElement.getText(),
Integer.parseInt(quantityElement.getText()));
}

ordercount++;
String orderId = ordercount + "";

order.put(orderId, orderItemTemp);

OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("orderprocessorns", "ns");
OMElement method = fac.createOMElement("createOrderResponse", omNs);
OMElement value = fac.createOMElement("orderId", omNs);
value.addChild(fac.createOMText(value, orderId));
method.addChild(value);

return method;
}

Data comes to the web service in the format of XML. To identify which method to call, the outer tag of the XML is the same as the name of the method. So the XML that comes to this method has a outer element with the name createOrder. We are writing the web service assuming that the XML which comes to web service has the bellow format.

<createOrder>
<item>
<itemId>A</itemId>
<quantity>1</quantity>
</item>
        <item>
<itemId>B</itemId>
<quantity>5</quantity>
</item>
</createOrder>

So the element is the object used to store the above XML data. So if you get the first element of the of the element, you will have a object having a XML of
  <item>
<itemId>A</itemId>
<quantity>1</quantity>
</item>

if you get the first child of orderItem you will get the itemId element. If you get the next sibling of that you will get the quantity element. You have to get the next sibling of the orderItem until you get a null object to get all the items of the order. You can create a HashMap and store the item and quantity in a HashMap. Then add the HashMap to the order HashMap with the generated key. The You have to generate a XML to send back the id of the order. It will have the bellow format.

<createOrderResponse>
<orderId>1</orderId>
</createOrderResponse>

Second method will be like this. You can get the details of the order when you send the id of the order.

  public OMElement getOrderDetails(OMElement element) {
element.build();
element.detach();

OMElement orderIdElement = element.getFirstElement();
String orderId = orderIdElement.getText();

HashMap<String, Integer> orderDetails = order.get(orderId);

OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("orderprocessor", "ns");
OMElement method = fac.createOMElement("getOrderDetails", omNs);

if (orderDetails != null) {
for (String key : orderDetails.keySet()) {
OMElement item = fac.createOMElement("item", omNs);

OMElement itemId = fac.createOMElement("itemId", omNs);
itemId.addChild(fac.createOMText(itemId, key));
item.addChild(itemId);

OMElement quantity = fac.createOMElement("quantity", omNs);
quantity.addChild(fac.createOMText(quantity,
"" + orderDetails.get(key)));
item.addChild(quantity);

method.addChild(item);
}
}

return method;
}

If you have the basic understanding of XML you can certainly understand what happens in the code.
Now you are finished with writing the business logic of the web service. Create a folder named META-INF in the root folder of the project and services.xml in that folder.
Add the bellow code to the services.xml file.

<?xml version="1.0" encoding="UTF-8"?>

<service name="OrderProcessor" scope="application">
    <description>
        Order Processor
    </description>
    <operation name="createOrder">
        <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
    </operation>
    <operation name="getOrderDetails">
        <messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
    <parameter name="ServiceClass">service.OrderProcessor</parameter>
</service>

If you carefully read this you can understand what each of this means. You have to be very careful when creating the services.xml file. Web service will not work properly with errors in this file.

Then right click on the project and click Export. Select JAR file as the format and give name OrderProcessor.aar as the file name. Then export the .aar file to a desired location. Then copy the .aar file to "tomcat7/webapps/axis2/WEB-INF/services". Then restart the server. If you go to the "http://localhost:8080/axis2/services/listServices" you will be able to see the web service you created is listed there. If you click on the web service name you will be able to see the wsdl file of the web service. This file is created automatically according to the method signatures of your web service. If you want to have a wsdl file different from that you can manually create the wsdl file. Normally wsdl file is used to get the details about web service and then create a client. But as you know the details about the web service you don't want to read the wsdl file. But when you actually create a web service client, you may not know the implementation details about the web service. So you have to go through the wsdl file and get the knowledge about what methods are there and how the data must be formatted and so on.

Now we are going to create sample client to invoke the web service. It is a ordinary java project and for this also you have to import the JARs imported when you created the web service.

import java.util.HashMap;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;

public class OrderProcessClient {
private static EndpointReference targetEPR = new EndpointReference(
"http://localhost:8080/axis2/services/OrderProcessor");

public static void main(String[] args) {
try {
Options options = new Options();
options.setTo(targetEPR);
options.setTransportInProtocol(Constants.TRANSPORT_HTTP);

ServiceClient sender;
sender = new ServiceClient();
sender.setOptions(options);

String orderId = createOrder(sender);
OMElement orderResult = getOrderDetails(sender, orderId);

System.out.print("Order id - ");
System.out.println(orderId);
System.out.println("Order items");
printOrder(orderResult);

} catch (AxisFault e) {
e.printStackTrace();
}

}

public static OMElement createOrderPayload(HashMap<String, Integer> order) {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("orderprocessorns", "ns");
OMElement method = fac.createOMElement("createOrder", omNs);

for (String key : order.keySet()) {
OMElement item = fac.createOMElement("item", omNs);

OMElement itemId = fac.createOMElement("itemId", omNs);
itemId.addChild(fac.createOMText(itemId, key));
item.addChild(itemId);

OMElement quantity = fac.createOMElement("quantity", omNs);
quantity.addChild(fac.createOMText(quantity, "" + order.get(key)));
item.addChild(quantity);

method.addChild(item);
}

return method;
}

public static OMElement getOrderDetailsPayload(String id) {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("orderprocessorns", "ns");

OMElement method = fac.createOMElement("getOrderDetails", omNs);
OMElement orderId = fac.createOMElement("orderId", omNs);
orderId.addChild(fac.createOMText(orderId, id));
method.addChild(orderId);
return method;
}

public static String createOrder(ServiceClient sender) {
HashMap<String, Integer> order = new HashMap<>();
order.put("a", 3);
order.put("f", 2);
order.put("e", 1);
order.put("c", 5);

String orderId = "";

OMElement orderPayload = createOrderPayload(order);
OMElement result;
try {
result = sender.sendReceive(orderPayload);
orderId = result.getFirstElement().getText();
} catch (AxisFault e) {
e.printStackTrace();
}

return orderId;

}

public static OMElement getOrderDetails(ServiceClient sender, String orderId) {
OMElement orderDetails = getOrderDetailsPayload(orderId);
OMElement orderResult;
try {
orderResult = sender.sendReceive(orderDetails);
} catch (AxisFault e) {
OMFactory fac = OMAbstractFactory.getOMFactory();
OMNamespace omNs = fac.createOMNamespace("orderprocessorns", "ns");
orderResult = fac.createOMElement("wrong", omNs);
orderResult.addChild(fac.createOMText(orderResult, "wrong"));
e.printStackTrace();
}

return orderResult;
}

public static void printOrder(OMElement element) {
element.build();

if (((OMElement) element.getFirstElement()) != null) {

element.detach();

OMElement orderItem = (OMElement) element.getFirstElement();
OMElement itemElement;
OMElement quantityElement;

do {
itemElement = (OMElement) orderItem.getFirstOMChild();
quantityElement = (OMElement) itemElement.getNextOMSibling();
System.out.println(itemElement.getText() + " - "
+ Integer.parseInt(quantityElement.getText()));
} while ((orderItem = (OMElement) orderItem.getNextOMSibling()) != null);
} else {
System.out.println("No such order");
}

}

}

Now you have created the client. You can run the project to invoke the web service. You can add more methods to the web service and call them using the client.

No comments:

Post a Comment