Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Creating a Simple Java REST Service Using HTTP-RPC

DZone's Guide to

Creating a Simple Java REST Service Using HTTP-RPC

We take a look at how to implement a Java-based web service using the HTTP-RPC framework. Read on to find out how and for some example code.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.


NOTE 10/22/2016 Support for implementing REST services was dropped from the HTTP-RPC project in version 3.6. However, many of the features described in this article have been migrated to the JTemplate project.

HTTP-RPC is an open-source framework for simplifying development of REST applications. It allows developers to create and access web services using a convenient, RPC-like metaphor while preserving fundamental REST principles such as statelessness and uniform resource access. The project currently includes support for implementing REST services in Java and consuming services in Java, Objective-C/Swift, or JavaScript.

HTTP-RPC services are accessed by applying an HTTP verb such as GET or POST to a target resource. Arguments are provided either via the query string or in the request body, like an HTML form. Results are generally returned as JSON, although 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.

This article provides an overview of how HTTP-RPC can be used to create a simple REST service.

WebService Class

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;
    }
}

Arguments may be any numeric or boolean type,  String ,  java.net.URL , or  java.util.List.  URL arguments represent binary content and can only be used with POST requests. List arguments represent multi-value parameters. They may be used with any request type, but elements must be a supported simple type; e.g. List<Double> or List<URL>.

Methods may return any numeric or boolean type, CharSequence, java.util.List or java.util.Map. Results are mapped to their JSON equivalents as follows:

  • numeric primitive/ Number: number
  • boolean/Boolean: true/false
  • CharSequence: string
  • java.util.List: array
  • java.util.Map: object

Methods may also return void to indicate that they do not produce a value.

List and Map types are not required to support random access; iterability is sufficient. This allows service implementations to stream collection data rather than buffering it in memory before it is written. Additionally, collection types that implement the AutoCloseable interface will be automatically closed after their contents have been written to the output stream, ensuring that system resources are not leaked.

ProductService Class

The example service provides CRUD (“create, read, update, and delete”) access to a simple product database. It extends WebService and provides a collection of methods for managing the database’s content:

public class ProductService extends WebService {
    ...
}

The underlying product data is stored in the Products table 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)
);

GET

The getProducts() method returns the product list. It is associated with the HTTP GET operation:

@RPC(method="GET", path="products")
public ResultSetAdapter getProducts() throws SQLException {
    Statement statement = getConnection().createStatement();

    return new ResultSetAdapter(statement.executeQuery("SELECT * FROM Products"));
}

The ResultSetAdapter class allows the result of a SQL query to be efficiently returned from a service method. This class 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. It also implements AutoCloseable, to ensure that the underlying result set is closed once all of the response data has been written.

Further, ResultSetAdapter is forward-scrolling only; its contents are not accessible via the get() and size() methods. This allows query results to be returned to the caller directly, without any intermediate buffering.

A response produced by the method might look something like this, where each object in the array represents a row from the result set:

[
  {
    "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
  },
  ...
]

POST

The addProduct() method is associated with the HTTP POST operation. It inserts a new row into the Products table. Product information is provided by the method arguments:

@RPC(method="POST", path="products")
public void addProduct(String productCode, String productName,
    String productLine, String productScale, String productVendor, String productDescription,
    int quantityInStock, double buyPrice, double MSRP) throws SQLException {
    String sql = "INSERT INTO Products (productCode, productName,"
        + " productLine, productScale, productVendor, productDescription,"
        + " quantityInStock, buyPrice, MSRP) "
        + "VALUES (:productCode, :productName,"
        + " :productLine, :productScale, :productVendor, :productDescription,"
        + " :quantityInStock, :buyPrice, :MSRP)";

    Parameters parameters = Parameters.parse(sql);
    PreparedStatement statement = getConnection().prepareStatement(parameters.getSQL());

    parameters.apply(statement, mapOf(
        entry("productCode", productCode),
        entry("productName", productName),
        entry("productLine", productLine),
        entry("productScale", productScale),
        entry("productVendor", productVendor),
        entry("productDescription", productDescription),
        entry("quantityInStock", quantityInStock),
        entry("buyPrice", buyPrice),
        entry("MSRP", MSRP)
    ));

    statement.execute();
}

The Parameters class provides a means for executing prepared statements using JPQL-like named parameter values rather than indexed arguments. The Parameters#getSQL() method returns the parsed SQL in standard JDBC syntax. This value is used to create the actual prepared statement.

mapOf() and  entry()  are static convenience methods provided by the WebService class for simplifying map creation.

PUT

The updateProduct() method is associated with the HTTP PUT operation. It allows a caller to update the inventory level of a given product:

@RPC(method="PUT", path="products")
public void updateProduct(String productCode, int quantityInStock) throws SQLException {
    String sql = "UPDATE Products SET quantityInStock = :quantityInStock WHERE productCode = :productCode";

    Parameters parameters = Parameters.parse(sql);
    PreparedStatement statement = getConnection().prepareStatement(parameters.getSQL());

    parameters.apply(statement, mapOf(
        entry("productCode", productCode),
        entry("quantityInStock", quantityInStock)
    ));

    statement.execute();
}

This method could be overloaded to provide additional update capabilities, such as changing a price or product description.

DELETE

Finally, deleteProduct() is associated with the HTTP DELETE method. It simply deletes a row from the Products table:

@RPC(method="DELETE", path="products")
public void deleteProduct(String productCode) throws SQLException {
    String sql = "DELETE FROM Products WHERE productCode = :productCode";

    Parameters parameters = Parameters.parse(sql);
    PreparedStatement statement = getConnection().prepareStatement(parameters.getSQL());

    parameters.apply(statement, mapOf(entry("productCode", productCode)));

    statement.execute();
}

More Information

This article introduced the HTTP-RPC framework and provided an example of how it can be used to create a simple REST service. The complete source code for the sample service can be found here.

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

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
methods ,implementation ,services ,sample ,rest ,database ,http-rpc ,web ,service ,class

Published at DZone with permission of Greg Brown, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}