XINS - Calling Convention Primer

About this tutorial

This tutorial shows how to extend XINS with your own custom protocol, step by step. The exercise should take you less than 15 minutes.

Prerequisites

You should have finished the XINS Primer as we will continue where the latter left off.

Use XINS 3.1.

Overview of contents

This article is divided in the following sections:

1. Introduction

The XINS framework supports different protocols, including XML-RPC, SOAP and POX-RPC. In XINS terminology, these are called calling conventions. Different calling conventions live next to each other at run-time. When a request comes in, the framework determines which calling convention should handle the request:

XINS can be extended with custom calling conventions. Such a calling convention has the following responsibilities:

  1. indicate which HTTP methods it supports (defaults to GET, HEAD and POST);
  2. indicate for a specific HTTP request whether it can handle it (optional);
  3. interpret an HTTP request and convert it to a XINS request object;
  4. convert a XINS response object to an HTTP response.

2. Create the Java file

Let's prepare to create the initial version of the Java source file. A minimal custom calling convention implementation must:

Step 1. Remember that the XINS Primer let you create a small XINS project? You need that now.

Under the apis/myapi/impl/com/mycompany directory, create a new directory called cc.

Step 2. In that cc directory, create a file called MyCallingConvention.java with the following contents:
package com.mycompany.cc;

import java.io.IOException;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.xins.common.Log;
import org.xins.common.collections.PropertyReader;
import org.xins.common.servlet.ServletRequestPropertyReader;
import org.xins.common.text.ParseException;
import org.xins.server.CustomCallingConvention;
import org.xins.server.FunctionNotSpecifiedException;
import org.xins.server.FunctionRequest;
import org.xins.server.FunctionResult;
import org.xins.server.InvalidRequestException;

public class MyCallingConvention extends CustomCallingConvention {

   public MyCallingConvention() {
      // empty
   }
}

3. Analyze the incoming request

The convertRequestImpl method gets an HttpServletRequest object as input, based on which it needs to determine:

  • which function should be called;
  • which parameters, if any, should be passed to that function;
  • is there a data section to pass to the function?

This information will then be used to invoke the correct function, with the appropriate arguments.

Visually:

This information then needs to be returned to the XINS server engine in a FunctionRequest object.

Our example implementation expects the function in an HTTP parameter named _function and it uses the other HTTP parameters as parameters for the functions. For the sake of simplicity, it does not support an input data section.

Step 3. Add the following code in your class:
protected FunctionRequest convertRequestImpl(HttpServletRequest httpRequest)
throws InvalidRequestException, FunctionNotSpecifiedException {

   // Trace this method call
   Log.log_1053("In MyCallingConvention.convertRequestImpl(...)");

   // Determine which function should be invoked
   String function = httpRequest.getParameter("_function");

   // The function name must be specified
   if (function == null || "".equals(function)) {
      throw new FunctionNotSpecifiedException();
   }

   // Directly convert the parameters to a PropertyReader object
   PropertyReader params;
   try {
      params = new ServletRequestPropertyReader(httpRequest);
   } catch (ParseException exception) {
      throw new InvalidRequestException("Failed to parse request.",
                                        exception);
   }

   // XXX: The input data section is not supported

   // Return an appropriate XINS request object
   return new FunctionRequest(function, params, null);
}

4. Produce the response

Once the function has processed the request, the framework hands the result to the convertResultImpl method in the calling convention. The latter converts it to an HTTP response, which is then returned to the client:

The result information comes in as a FunctionResult object.

Our example implementation will take the result and convert it to a simple HTML page that lists the output parameters with their respective values.

Step 4. Now add the following code in your class:
protected void convertResultImpl(FunctionResult      xinsResult,
                                 HttpServletResponse httpResponse,
                                 HttpServletRequest  httpRequest)
throws IOException {

   // Trace this method call
   Log.log_1053("In MyCallingConvention.convertResultImpl(...)");

   // Generate HTML
   String html = generateHTML(xinsResult);

   // Set HTTP status and add headers
   httpResponse.setStatus(HttpServletResponse.SC_OK);
   httpResponse.setContentType("text/html; charset=UTF-8");
   httpResponse.addIntHeader("Content-Length", html.length());

   // Write the body, except for HTTP HEAD requests
   if (! "HEAD".equals(httpRequest.getMethod())) {
      httpResponse.getWriter().print(html);
   }
}

private String generateHTML(FunctionResult result) {

   // Determine error code and output parameters
   String         error  = result.getErrorCode();
   PropertyReader params = result.getParameters();

   // Top part
   String html = "<html><head><body>"
               + "<h3>Error code</h3>"
               + "The error code is: "
               + error
               + "<h3>Output parameters</h3>"
               + "The following output parameters are set:"
               + "<table border=1><tr>"
               + "<th>Name</th>"
               + "<th>Value</th>"
               + "</tr>";

   // One table row per parameter
   Iterator names = params.getNames();
   while (names.hasNext()) {
      String name  = (String) names.next();
      String value = params.get(name);
      html += "<tr><td>" + name + "</td><td>" + value + "</td></tr>";
   }

   // Bottom part
   html += "</table></body></html>";

   return html;
}

5. Register the calling convention

Each calling convention needs to have a unique name. This name can be passed in a request, to explicitly specify that this is the one to be used. The name may contain letters, digits and underscores, but it should start with a letter.

For this exercise we will use the name:

  • mycc

Edit impl.xml

In order for the framework to know about a custom calling convention, it must be registered in the impl.xml file for the appropriate API.

Step 5 Under the MyProject directory, edit the apis/myapi/impl/impl.xml file and add a <calling-convention> tag as follows:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE impl PUBLIC
 "-//XINS//DTD Implementation 2.0//EN"
 "http://www.xins.org/dtd/impl_2_0.dtd">

<impl>
   <calling-convention
   name="mycc"
   class="com.mycompany.cc.MyCallingConvention"/>
</impl>

6. Test

Step 6 From the MyProject directory, start the internal server:
xins -Dorg.xins.server.config=xins.properties run-myapi

In the startup log, notice the following entry:

3245 INFO Default calling convention is "mycc".

Note: This log message is documented on-line, along with all other log messages produced by XINS.

Step 7 With a browser visit the following URL (assuming you deployed the application on the local machine):

Notice that the calling convention to use is explicitly set to _xins-std.

You should see an XML document as output.

Meanwhile, your log should show output similar to the following:

3521 INFO Received HTTP GET request from 127.0.0.1, path is "/myapi/", query string is "_function=SayHello&name=John&_convention=_xins-std".
3540 INFO 20060904-223347654 127.0.0.1 SayHello 0 0 name=John greeting=Hello+John.
Step 8 Let's change the calling convention from _xins-std to mycc; with your browser visit:

You should now see an HTML document, produced by your very own calling convention.

Step 9 Finally, let's see what XINS does when we do not explicitly indicate which calling convention should be used. Point your browser to:

As indicated in the startup log (see step 6), XINS defaults to the mycc calling convention, if that one is able to handle the request.

7. Finally

For more information about custom calling conventions, study the Javadoc API documentation for the CustomCallingConvention class.