Creating a JSON client for AXIS2

This post will try to explain how to develop a sample for Axis2 that uses the JSON module in Axis2. Before you read this blog post read this article to get a understanding about Axis2 JSON module
 
Creating the folder structure

Before we start to do anything lets create the folder structure that will be needed to create the sample. For this sample we will create a folder structure simmilar to the structure given below







The resources folder will contain the axis2.xml and the Java code will be added in to src/samples/googleservices/JSONSearch. We will look into each part separately.

Before we start working on the sample there are a few simple things that we have to do. Since we will be using the Yahoo Boss API to retrive the search results in JSON format, we need to acquire a API-Key that is needed when using the Boss API. You can create a Yahoo Boss API-Key here, this should not take more than 5 minutes if you already have a Yahoo account.
Now that we have a Yahoo Boss API-Key we can go onto implementing the sample. First lets take a look at the java class that does all the logic of the sample we will name the class "JSONSearchModel". 

package sample.yahooservices.JSONSearch;
package sample.yahooservices.JSONSearch;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axis2.Constants;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;

import java.io.UnsupportedEncodingException;
import java.io.File;
import java.net.URLEncoder;
import java.util.Iterator;

public class JSONSearchModel {
    /**
     * HTML Header to desplay snippet text
     */
    private String beginHTML = "Search Results";

    /**
     * HTML footer
     */
    private String endHTML = "";

    /**
     * Store the texts read by NavigationURL of soap
     */
    private String snippet;

    /**
    * The App id 
    */
    private final static String appIdString = "YOUR_API_KEY";

    public String searchYahoo(String query, String format) {
        try {
            snippet = beginHTML;
     query = query.replaceAll(" ","%20");
            String epr = "http://boss.yahooapis.com/ysearch/web/v1/"+query;
            File configFile = new File("resources/axis2.xml");
            ConfigurationContext configurationContext = ConfigurationContextFactory
                    .createConfigurationContextFromFileSystem(null, configFile
                            .getAbsolutePath());

            ServiceClient client = new ServiceClient(configurationContext, null);
            Options options = new Options();
            client.setOptions(options);
            options.setTo(new EndpointReference(epr));
            options.setProperty(Constants.Configuration.MESSAGE_TYPE, HTTPConstants.MEDIA_TYPE_X_WWW_FORM);
            options.setProperty(Constants.Configuration.HTTP_METHOD, Constants.Configuration.HTTP_METHOD_GET);
            OMElement response = client.sendReceive(getPayloadForYahooSearchCall(query, format));
            generateSnippet(response);
            return snippet;

        } catch (Exception e) {
            e.printStackTrace();
            snippet = "<h2>Error occurred during the invocation to Yahoo search service</h2>" +
                    "" + e.getMessage() + "
" + endHTML;
        }
        return snippet;
    }

    private static OMElement getPayloadForYahooSearchCall(String queryStr, String formatStr) throws UnsupportedEncodingException {
        OMFactory fac = OMAbstractFactory.getOMFactory();
        OMElement rootElement = fac.createOMElement("webSearch", null);

        OMElement appId = fac.createOMElement("appid", null, rootElement);
        appId.setText(appIdString);

        OMElement outputType = fac.createOMElement("format", null, rootElement);
        outputType.setText("json");

        if (formatStr != null && formatStr.length() != 0) {
            OMElement format = fac.createOMElement("format", null, rootElement);
            format.setText(URLEncoder.encode(formatStr, "UTF-8"));
        }
        return rootElement;
    }

    private void generateSnippet(OMElement response) {
        String title = null;
        String summary = null;
        String clickUrl = null;
        String url = null;
        OMElement result = null;
        //get an iterator for Result elements
        Iterator itr = response.getChildElements();
        Iterator innerItr;
        while (itr.hasNext()) {
            result = (OMElement) itr.next();
            innerItr = result.getChildElements();
            if (innerItr.hasNext()) {
                title = ((OMElement) innerItr.next()).getText();
                summary = ((OMElement) innerItr.next()).getText();
                url = ((OMElement) innerItr.next()).getText();
                clickUrl = ((OMElement) innerItr.next()).getText();
                if (title != null) {
                    snippet += ""+title+""+summary+url;
                }
            }
        }
        snippet += endHTML;
    }
} 
 

This is the code snippet for the "JSONSearchModel" class, lets take a look at each part of the class separately. As you can see we have our API-Key as a string that will be used later.
now lets take a look at the method "searchYahoo" this method takes in two String parameters query and format, query is the search query specified by the user and the format is the data retrieval format in this case it would be JSON. Then using the query the endpoint reference is created(this is because the Boss API v1 does not specify the query as a parameter). Then a service client is created using configurations specified in the axis2.xml (we will look at the axis2.xml later) then the options are set such as the message type and Http method in this case that is GET. Then using the "getPayloadForYahooSearchCall" method to create a OMElement that has all the parameter including the API-Key and data format a request is send through the client and a response OMElement is received and this OMElement is parsed using method "generateSnippet" to create a  HTML snippet.

This snippet is displayed in the UI. The following two class are used to generate the user interface i will not go into explaining the logic in these classes.
package sample.yahooservices.JSONSearch;

import javax.swing.*;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.HyperlinkEvent;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class UserInterface extends JPanel implements HyperlinkListener {

    private JEditorPane jep;
    private JScrollPane scrollPane;

    private JTextField schField;
    private JTextField formatField;

    private JButton schButton;
    private JButton backButton;

    private JLabel schLabel;
    private JLabel formatLabel;


    private JSONSearchModel model;
    private JSONSearchClient parent;

    private String response;
    public UserInterface(JSONSearchClient parent) {
        this.parent = parent;
        model =  new JSONSearchModel();
        initComponents();

        schButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleSearch();
            }
        });

        backButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                handleBack();
            }
        });

        Container pane = parent.getContentPane();
        pane.setLayout(null);

        pane.add(schLabel);
        pane.add(schField);
        pane.add(formatLabel);
        pane.add(formatField);
        pane.add(backButton);
        pane.add(schButton);
        pane.add(scrollPane);
    }

    public void initComponents() {
        schLabel = new JLabel("Search for");
        schLabel.setBounds(20, 10, 80, 25);
        schField = new JTextField();
        schField.setBounds(90, 10, 250, 25);

        formatLabel = new JLabel("format");
        formatLabel.setBounds(350, 10, 50, 25);
        formatField = new JTextField();
        formatField.setBounds(400, 10, 100, 25);

        backButton = new JButton("Back to Results");
        backButton.setBounds(670, 10, 150, 25);
        backButton.setEnabled(false);

        schButton =  new JButton("Search");
        schButton.setBounds(510, 10, 150, 25);


        jep = new JEditorPane();
        jep.setEditable(false);
        jep.setContentType("text/html");
        jep.addHyperlinkListener(this);

        scrollPane = new JScrollPane(jep);
        scrollPane.setBounds(10, 80, (JSONSearchClient.width - 30), (JSONSearchClient.height - 160));


    }

    private void handleSearch(){
        String query = schField.getText();

        if(!query.equals("")){
            response = model.searchYahoo(query, formatField.getText());
            jep.setText(response);
        }
    }

    private void handleBack(){
        jep.setText(response);
        backButton.setEnabled(false);
    }

    public void hyperlinkUpdate(HyperlinkEvent he) {
        if (he.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
            try {
                jep.setPage(he.getURL());
                backButton.setEnabled(true);
            }
            catch (Exception e) {
                  JOptionPane.showMessageDialog(parent, "Page could not be loaded",
                                "Page Error", JOptionPane.ERROR_MESSAGE);
            }
        }

    }
}



package sample.yahooservices.JSONSearch;

import javax.swing.*;
import java.awt.*;

public class JSONSearchClient extends JFrame {
    public static int width;
    public static int height;

    public JSONSearchClient(String title) throws HeadlessException {
        super(title);

        this.getContentPane().add(new UserInterface(this));
        this.setVisible(true);
    }

    public static void main(String[] args) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        width = screenSize.width;
        height = screenSize.height;
        JSONSearchClient form = new JSONSearchClient("Axis2 Yahoo-JSON Search Client");


        int left = (width) / 2;
        int top = (height) / 2;
        form.setLocation(left, top);
        form.setSize(width, height);
        form.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        form.setVisible(true);
    }
} 

AXIS2.xml

Now lets take a look at the axis2.xml configuration file. This is where all the configuration information is specified. We will look at the important parts of the xml and explain why they are there.I have also removed the comments that were included in the axis2.xml but comments are there in the zip file of the sample.
<axisconfig name="AxisJava2.0">
    
    <parameter name="hotdeployment">true</parameter>
    <parameter name="hotupdate">false</parameter>
    <parameter name="enableMTOM">false</parameter>
    <parameter name="enableSwA">false</parameter>
    <parameter name="ConfigContextTimeoutInterval">30</parameter>
    <parameter name="sendStacktraceDetailsWithFaults">false</parameter>
    <parameter name="DrillDownToRootCauseForFaultReason">false</parameter>
    <parameter name="userName">admin</parameter>
    <parameter name="password">axis2</parameter>
    <parameter name="manageTransportSession">false</parameter>

    <parameter locked="true" name="enableRESTInAxis2MainServlet">false</parameter>

    <parameter locked="true" name="disableREST">false</parameter>

    <parameter locked="true" name="disableSeparateEndpointForREST">false</parameter>

    
    <messagereceivers>
        <messagereceiver class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver" mep="http://www.w3.org/2004/08/wsdl/in-only">
        <messagereceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver" mep="http://www.w3.org/2004/08/wsdl/in-out">
    </messagereceiver></messagereceiver></messagereceivers>
    
    
    <messageformatters>
        <messageformatter class="org.apache.axis2.transport.http.SOAPMessageFormatter" contenttype="application/soap+xml">
        <messageformatter class="org.apache.axis2.json.JSONMessageFormatter" contenttype="application/json">
        <messageformatter class="org.apache.axis2.json.JSONBadgerfishMessageFormatter" contenttype="application/json/badgerfish">
        <messageformatter class="org.apache.axis2.json.JSONMessageFormatter" contenttype="text/javascript">
        <messageformatter class="org.apache.axis2.transport.http.XFormURLEncodedFormatter" contenttype="application/x-www-form-urlencoded">
    </messageformatter></messageformatter></messageformatter></messageformatter></messageformatter></messageformatters>
    
    
    <messagebuilders>
        <messagebuilder class="org.apache.axis2.json.JSONOMBuilder" contenttype="application/json">
        <messagebuilder class="org.apache.axis2.json.JSONBadgerfishOMBuilder" contenttype="application/json/badgerfish">
        <messagebuilder class="org.apache.axis2.json.JSONOMBuilder" contenttype="text/javascript">
    </messagebuilder></messagebuilder></messagebuilder></messagebuilders>
    
    
    
    
    <transportreceiver class="org.apache.axis2.transport.http.SimpleHTTPServer" name="http">
        <parameter name="port">8080</parameter>
    </transportreceiver>
   
    
    <transportsender class="org.apache.axis2.transport.local.LocalTransportSender" name="local">
    <transportsender class="org.apache.axis2.transport.http.CommonsHTTPTransportSender" name="http">
        <parameter name="PROTOCOL">HTTP/1.1</parameter>
        <parameter name="Transfer-Encoding">chunked</parameter>
    </transportsender>
    <transportsender class="org.apache.axis2.transport.http.CommonsHTTPTransportSender" name="https">
        <parameter name="PROTOCOL">HTTP/1.1</parameter>
        <parameter name="Transfer-Encoding">chunked</parameter>
    </transportsender>

    
    <phaseorder type="InFlow">
        
        <phase name="Transport">
            <handler class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher" name="RequestURIBasedDispatcher">
                <order phase="Transport">
            </order></handler>
            <handler class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher" name="SOAPActionBasedDispatcher">
                <order phase="Transport">
            </order></handler>
        </phase>
        <phase name="Security">
        <phase name="PreDispatch">
        <phase class="org.apache.axis2.engine.DispatchPhase" name="Dispatch">
            <handler class="org.apache.axis2.dispatchers.AddressingBasedDispatcher" name="AddressingBasedDispatcher">
            <handler class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher" name="RequestURIOperationDispatcher">

            <handler class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher" name="SOAPMessageBodyBasedDispatcher">
        </handler></handler></handler></phase>
        
        
        <phase name="OperationInPhase">
 <phase name="soapmonitorPhase">
    </phase></phase></phase></phase></phaseorder>
    <phaseorder type="OutFlow">
        
 <phase name="soapmonitorPhase">
        <phase name="OperationOutPhase">
        
        
        <phase name="PolicyDetermination">
        <phase name="MessageOut">
        <phase name="Security">
    </phase></phase></phase></phase></phase></phaseorder>
    <phaseorder type="InFaultFlow">
        <phase name="PreDispatch">
        <phase class="org.apache.axis2.engine.DispatchPhase" name="Dispatch">
            <handler class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher" name="RequestURIBasedDispatcher">

            <handler class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher" name="SOAPActionBasedDispatcher">

            <handler class="org.apache.axis2.dispatchers.AddressingBasedDispatcher" name="AddressingBasedDispatcher">
            <handler class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher" name="RequestURIOperationDispatcher">

            <handler class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher" name="SOAPMessageBodyBasedDispatcher">
        </handler></handler></handler></handler></handler></phase>
        
        <phase name="OperationInFaultPhase">
 <phase name="soapmonitorPhase">
    </phase></phase></phase></phaseorder>
    <phaseorder type="OutFaultFlow">
        
 <phase name="soapmonitorPhase">
        <phase name="OperationOutFaultPhase">
        <phase name="PolicyDetermination">
        <phase name="MessageOut">
    </phase></phase></phase></phase></phaseorder>
</transportsender></axisconfig>
Now Lets take a look at some of the important parts of the xml.The first few lines are to set a few parameter that will be needed such as time outs and to enable and disable some parameters.
<messagereceivers>
        <messagereceiver class="org.apache.axis2.receivers.RawXMLINOnlyMessageReceiver" mep="http://www.w3.org/2004/08/wsdl/in-only"></messagereceiver>
        <messagereceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver" mep="http://www.w3.org/2004/08/wsdl/in-out"></messagereceiver>
    </messagereceivers>
This is the tag that sets the default message receivers for this service here "RawXMLINOnlyMessageReceiver" has to be used and the reason is due implementation decision if you want to read more about why "RawXMLINOnlyMessageReceiver" has to be used read this blog post.

<messageformatters>
        <messageformatter class="org.apache.axis2.transport.http.SOAPMessageFormatter" contenttype="application/soap+xml"></messageformatter>
        <messageformatter class="org.apache.axis2.json.JSONMessageFormatter" contenttype="application/json"></messageformatter>
        <messageformatter class="org.apache.axis2.json.JSONBadgerfishMessageFormatter" contenttype="application/json/badgerfish"></messageformatter>
        <messageformatter class="org.apache.axis2.json.JSONMessageFormatter" contenttype="text/javascript"></messageformatter>
        <messageformatter class="org.apache.axis2.transport.http.XFormURLEncodedFormatter" contenttype="application/x-www-form-urlencoded">
  </messageformatter>

</messageformatters>

This tag sets the Message formatters in this example we will be using the "XFormURLEncodedFormatter" which is mapped to the message type we set in the "JSONSearchModel" class when setting options for the service client.Although there are a few other message formatters defined in this xml the following part would be sufficent to run the sample
<messageformatters>
               <messageformatter class="org.apache.axis2.transport.http.XFormURLEncodedFormatter" contenttype="application/x-www-form-urlencoded">
    </messageformatter>
</messageformatters>

Since we are using a remote service, we have to know the exact JSON content type that is used by that service, and we have to use that content type in your client as well.The Yahoo search service sends the response as a “Mapped” formatted JSON string with the content type “text/javascript”. This content type is mapped with the JSONOMBuilder in the axis2.xml using the following lines.

<messagebuilders>
        <messagebuilder class="org.apache.axis2.json.JSONOMBuilder" contenttype="application/json"></messagebuilder>
        <messagebuilder class="org.apache.axis2.json.JSONBadgerfishOMBuilder" contenttype="application/json/badgerfish"></messagebuilder>
        <messagebuilder class="org.apache.axis2.json.JSONOMBuilder" contenttype="text/javascript"></messagebuilder>
  </messagebuilders>
There are a few more tags in the axis2.xml file but i will not try to explain them because they are out of the scope of this tutorial.
Now that we have all the parts ready all we need is the ant script that will run the sample. The ant script for this sample will look like this.
<project default="run.client" name="YahooJSONSearchSample">
 <property environment="env"></property>
 <property name="axis2.home" value="../../"></property>
 <property name="axis2.repo" value="${axis2.home}/repository"></property>
 <property name="build.dir" value="build"></property>
 <property name="client.classes.dir" value="${build.dir}/classes"></property>
 <path id="axis.classpath">
  <fileset dir="${axis2.home}/lib">
   <include name="*.jar">
  </include></fileset>
  <pathelement location="build/yahooJSONSearch.jar">
 </pathelement></path>

 <target name="compile">
  <mkdir dir="${client.classes.dir}">
  
  <javac destdir="${client.classes.dir}" srcdir="src">
   <classpath refid="axis.classpath">
  </classpath></javac>
  <jar destfile="${build.dir}/yahooJSONSearch.jar">
   <fileset dir="${build.dir}/classes">
  </fileset></jar>
 </mkdir></target>
 <target depends="compile" name="run.client">
  <java classname="sample.yahooservices.JSONSearch.JSONSearchClient" classpathref="axis.classpath" fork="true">
   <jvmarg value="-Daxis2.repo=${axis2.repo}">
  </jvmarg></java>
 </target>
 <target name="clean">
  <delete dir="build">
 </delete></target>
</project>

You can find the zipped source code of the sample here and you can download the latest Axis2 version here.

Comments

Popular posts from this blog

Writing Unit Tests to test JMS Queue listener code with ActiveMQ

Reading and Writing Binary files in java - Converting Endianness ( between Big-endian byte order and Little-endian byte order)

Setting up Heron Cluster with Apache Aurora Locally