RESTing Easy

By: Martin Serrano

Introduction

Modularity is a hallmark of good application development practice. When attempting to rapidly implement a web-based application, basing the front end on one or more REST APIs is a common choice. A REST API is simple to use and provides for easy client-side bookmarking. Since most of our customers develop some type of custom web UI for AIE-based applications, we've made it simple to build robust, testable REST APIs.

Building a REST API in AIE requires extending PlatformComponent and overriding two methods. Testing the API involves a few lines of code and use of shipped test framework classes (the same we use to test AIE). In the example below, I create a REST API that provides the current total memory and memory in use by the hosting JVM.

A simple REST API

Creating an AIE service that provides a REST API is as simple as extending the PlatformComponent class and overriding a couple of methods.

Override convertCGIRequest

Whenever a service that exposes an HTTP endpoint is accessed with a GET request, the request is converted by AIE into a CgiRequest message. CgiRequest messages contain all of the parameters and headers of the request. The convertCgiRequest method is a hook for the developer to translate the request into a message that the REST service can handle. This can be as simple as translating the request into a StringMessage with the relevant data, but I prefer to use simple inner classes to make requests concrete instead of opaque (this also makes testing easier). Returning null from this method indicates the CGI request is not recognized and the service should return nothing. Alternatively, you can throw an exception that will result in an error getting returned to the caller.

Sample GET request

http://localhost:17001/memtracker?mem=true

Overridden class

/** {@inheritDoc} */
  @Override
  protected PlatformMessage convertCgiRequest(CgiRequest cgi) throws AttivioException {
    String mem = cgi.getCgiParameter("mem");
    if (mem != null) {
      return new MemRequestMessage();  // concrete private message class
    } else {
      return null; // no valid request found... alternatively could throw an exception
    }
  }

The inner message class

public static class MemRequestMessage extends AbstractPlatformMessage {}

Override handleMessage

handleMessage()

/** {@inheritDoc} */
  @Override
  protected PlatformMessage handleMessage(MessageContext context, PlatformMessage msg)
  throws AttivioException {if (msg instanceof MemRequestMessage) {
      return new CgiResponse() {
        @Override
        public void writeResponse(HttpServletResponse resp) throws IOException {
          resp.getWriter().format("%d,%d", Runtime.getRuntime().freeMemory(), 
          Runtime.getRuntime().maxMemory());
        }
      };
    }
  }

Testing

As has been pointed out here, here and here, at Attivio we really believe in testing. We support that philosophy by making it as easy (fast to write/fast to run) to test functionality as possible. To that end, we have developed supporting classes for testing PlatformComponents (including services) without having to start a full AIE system. With the AIE SDK, we ship those supporting classes so that our customers can test easily as well.

Testing the Service

@Test
  public void cgiTest() throws AttivioException, IOException {
    MemoryApiService srv = new MemoryApiService();
    TransformerTestUtils.startTransformer(srv); // mock-up system stuff and start 
    the service
    
    CgiRequest req = new CgiRequest(null);
    req.setCgiParameters("mem=", IOUtils.DEFAULT_ENCODING); // provide the full 
    cgi query string we are testing, use UTF-8 encoding
    CgiResponse respMessage = (CgiResponse) srv.onCall(null, req); // 
    execute the request
    MockHttpServletResponse mockServletResponse = new MockHttpServletResponse();
    respMessage.writeResponse(mockServletResponse);

    String[] results = mockServletResponse.getWrittenResponse().split(",");
    Assert.assertTrue(Long.parseLong(results[0]) < Long.parseLong(results[1]));
    Assert.assertTrue(Long.parseLong(results[0]) > 0);
 }

The MockHttpServletResponse that is available in AIE 3.1 is a very simple class. Below is a partial implementation for your use if needed.

/**
   * A mocked version of a HttpServletResponse
   */
  public class MockHttpServletResponse implements HttpServletResponse {
    private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    private final PrintWriter writer = new PrintWriter(bos, true);
    /**
     * @return the response written so far
     */
    public String getWrittenResponse() {
      return bos.toString();
    }
    /** {@inheritDoc} */
    @Override
    public PrintWriter getWriter() throws IOException {
      return writer;
    }
   ////  rest of default (unchanged from Eclipse auto-generation) implementation 
   omitted for brevity.

Comments
Blog post currently doesn't have any comments.
Leave comment



 Security code