BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Using Templates to Transform Web Service Results into Markup

Using Templates to Transform Web Service Results into Markup

Key takeaways

  • HTTP-RPC is an open-source framework for building REST services in Java
  • Can service web, mobile, and desktop clients from a single codebase
  • Provides JSON response data by default
  • Supports HTML-based resource representations via templates
  • Can also generate XML, CSV, etc.

Web services are a means of implementing application logic that can be accessed remotely via HTTP. They allow distributed, often heterogenous, clients to interact with server-side functionality, and are commonly used to provide a back-end API for mobile applications. However, they are often implemented using a separate software stack from an application's web front end, resulting in additional development and maintenance effort.

Templates are a means of separating data from presentation. They allow the final output format for a data structure to be specified independently of the data itself, promoting a clear separation of responsibility. In MVC terminology, the template represents the view, and the data structure the model. The web service acts as the controller, accepting input from the caller, generating the model data, and applying the template to the model to produce the final result.

This article describes how templates written using the CTemplate system (aka Mustache) can be used to transform the output of web services implemented using the HTTP-RPC framework into HTML. This allows an application to efficiently service both web and mobile clients from a single codebase, significantly reducing overall development effort.

HTTP-RPC

HTTP-RPC is an open-source framework for simplifying development of REST-based applications. It allows developers to create and access HTTP-based web services using a convenient, RPC-like metaphor while preserving fundamental REST principles such as statelessness and uniform resource access. For more information on HTTP-RPC, my previous article provided a broad overview.

HTTP-RPC services are accessed by applying an HTTP verb such as GET or POST to a target resource. The target is specified by a path representing the name of the resource, and is generally expressed as a noun such as /calendar or /contacts.

Arguments are provided either via the query string or in the request body, like an HTML form. Although services may produce any type of content, results are generally returned as JSON. Operations that do not return a value are also supported.

For example, the following request might retrieve the sum of two numbers, whose values are specified by the “a” and “b” query arguments:

GET /math/sum?a=2&b=4

The service would return the value 6 in response.

Example Service

WebService is an abstract base class for HTTP-RPC web services. Service operations are defined by adding public methods to a concrete service implementation.

The @RPC annotation is used to flag a method as remotely accessible. This annotation associates an HTTP verb and a resource path with the method. All public annotated methods automatically become available for remote execution when the service is published.

For example, the following class might be used to implement the simple addition operation discussed in the previous section:

public class MathService extends WebService {
    @RPC(method="GET", path="sum")
    public double getSum(double a, double b) {
        return a + b;
    }
}

As another example, consider the following method, which calculates a set of simple statistical values:

@RPC(method="GET", path="statistics")
public Map<String, ?> getStatistics(List<Double> values) {
    int count = values.size();

    double sum = 0;

    for (double value : values) {
        sum += value;
    }

    double average = sum / count;

    return mapOf(
        entry("count", count), 
        entry("sum", sum), 
        entry("average", average)
    );
}

A GET for this URL would invoke the method, passing a list argument containing the values 1, 3, and 5:

/math/statistics?values=1&values=3&values=5

The result would be returned to the caller as follows:

{
  "count": 3, 
  "sum": 9.0,
  "average": 3.0
}

CTemplate

Although data produced by an HTTP-RPC web service is typically returned as JSON, it may be more convenient in some situations to return the results to the caller in a different format; for example, as HTML to support a browser-based client application. HTTP-RPC natively provides support for templates defined using the CTemplate system, allowing arbitrary transformations to be applied to method results.

CTemplate defines a set of "markers" that are replaced with values supplied by a "data dictionary" when the template is processed:

  • {{_variable_}} - injects a variable from the data dictionary into the output
  • {{#_section_}}...{{/_section_}} - defines a repeating section of content
  • {{>include}} - imports content specified by another template
  • {{!_comment_}} - provides informational text about a template's content

The value returned by a service method represents the data dictionary. Usually, this will be an instance of java.util.Map whose keys represent the values provided by the dictionary.

A simple template for presenting the result of the getStatistics() method as a web page is shown below:

<html>
<head>
    <title>Statistics</title>
</head>
<body>
    <p>Count: {{count}}</p>
    <p>Sum: {{sum}}</p>
    <p>Average: {{average}}</p> 
</body>
</html>

Note the use of the variable markers for the "count", "sum", and "average" values. At execution time, these markers will be replaced by the corresponding values from the data dictionary (i.e. the map value returned by the method) to produce the final output.

Template Annotation

The @Template annotation is used to associate a template document with a method. The annotation's value represents the name and type of the template that will be applied to the results. For example:

@RPC(method="GET", path="statistics")
@Template(name="statistics.html", mimeType="text/html")
public Map<String, ?> getStatistics(List<Double> values) { ... }

The “name” element refers to the file containing the template definition. It is specified as a resource path relative to the service type.

The “contentType” element indicates the type of the content produced by the named template. It is used by HTTP-RPC to identify the requested template. A specific representation is requested by appending a file extension associated with the desired MIME type to the service name in the URL.

With the annotation applied, a GET for statistics.html would invoke the getStatistics() method and apply the template to the results:

<html>
<head>
    <title>Statistics</title>
</head>
<body>
    <p>Count: 3.0</p>
    <p>Sum: 9.0</p>
    <p>Average: 3.0</p> 
</body>
</html>

The result is identical to the output of the following command, which uses curl to download the raw JSON response and then applies the template using the mustache command:

curl -s "http://localhost/math/statistics?values=1&values=3&values=5" | mustache - statistics.html

Since HTTP-RPC applies the template on the server, no client-side processing is necessary. However, curl is a great way to test templates during development.

A Practical Example

For a more practical example, consider a web service that returns the result of a SQL query on the following table, taken from the BIRT sample database:

CREATE TABLE Products (
  productCode VARCHAR(50) NOT NULL,
  productName VARCHAR(70) NOT NULL,
  productLine VARCHAR(50) NOT NULL,
  productScale VARCHAR(10) NOT NULL,
  productVendor VARCHAR(50) NOT NULL,
  productDescription TEXT NOT NULL,
  quantityInStock SMALLINT NOT NULL,
  buyPrice DOUBLE NOT NULL,
  MSRP DOUBLE NOT NULL,
  PRIMARY KEY (productCode)
);

A service method that returns the contents of this table might be defined as shown below. The ResultSetAdapter class allows the result of a SQL query to be efficiently returned from a service method. It implements the List interface and makes each row in a JDBC result set appear as an instance of Map, rendering the data suitable for serialization to JSON. HTTP-RPC ensures that the result set is closed once all of the data has been written:

@RPC(method="GET")
@Template(name="products.html", contentType="text/html")
public ResultSetAdapter getProducts() throws SQLException {
    Statement statement = getConnection().createStatement();
    String sql = "SELECT * FROM Products";

    return new ResultSetAdapter(statement.executeQuery(sql));
}

The raw JSON output of the service method might look something like this, which could be easily consumed by a mobile application:

[
  {
    "productCode": "S10_1678",
    "productName": "1969 Harley Davidson Ultimate Chopper",
    "productLine": "Motorcycles",
    "productScale": "1:10",
    "productVendor": "Min Lin Diecast",
    "productDescription": "This replica features working kickstand...",
    "quantityInStock": 7932,
    "buyPrice": 48.81,
    "MSRP": 95.7
  },
  ...
]

The following template could be used to transform the results to HTML, suitable for presentation in a web browser. Note that the example uses the “^html” modifier to ensure that the output is properly HTML-encoded. Modifiers are discussed in more detail in the project documentation:

<html>
<head>
    <title>Product List</title>
</head>
<body>
    <table>
    {{#.}}<tr>
    <td>{{productCode:^html}}</td>
    <td>{{productName:^html}}</td>
    <td>{{productLine:^html}}</td>
    <td>{{productScale:^html}}</td>
    <td>{{productVendor:^html}}</td>
    <td>{{productDescription:^html}}</td>
    <td>{{quantityInStock}}</td>
    <td>{{buyPrice:format=currency}}</td>
    <td>{{MSRP:format=currency}}</td>
    </tr>{{/.}}
    </table>
</body>
</html>

The resulting output might appear as follows:

<html>
<head>
    <title>Product List</title>
</head>
<body>
    <table>
    <tr>
    <td>S10_1678</td>
    <td>1969 Harley Davidson Ultimate Chopper</td>
    <td>Motorcycles</td>
    <td>1:10</td>
    <td>Min Lin Diecast</td>
    <td>This replica features working kickstand...</td>
    <td>7932</td>
    <td>$48.81</td>
    <td>$95.70</td>
    </tr>
    ...
    </table>
</body>
</html>

Summary

This article provided an overview of how templates can be used to transform REST service results into HTML, significantly reducing the development effort typically associated with supporting both web and mobile client applications. Note that templates are not limited to HTML; they can be used to easily generate other data representations as well, such as XML or CSV.

The latest version of HTTP-RPC can be downloaded here. For more information, see the project README.

About the Author

Greg Brown is a software engineer with over 20 years of experience in consulting, product, and open-source development. His current focus is  on mobile applications and REST services.

 

 

 

Rate this Article

Adoption
Style

BT