HTTP-RPC 3.4 Released

HTTP-RPC 3.4 is now available for download. This release improves support for date/time types. Java 8 temporal types including LocalDate, LocalTime, and LocalDateTime are now supported both as argument types and return values. java.util.Date values can also now be used as method arguments (previous releases only supported returning Date values).

Additionally, this release provides support for nested structures in JDBC query results. For example, the following query might be used to retrieve a list of employees:

SELECT first_name AS 'name.first', last_name AS 'name.last', title FROM employees

Because the aliases for the first_name and last_name columns contain a period, each row will contain a nested “name” structure instead of a flat collection of key/value pairs; for example:

[
  {
    "name": {
      "first": "John",
      "last": "Smith"
    },
    "title": "Manager"
  },
  ...
]

Finally, all iOS code samples have been updated to work with Xcode 8 and Swift 3.

For more information, see the project README.

MarkupKit: Declarative UI for iOS

MarkupKit is an open-source framework for simplifying development of native iOS applications. It allows developers to construct user interfaces declaratively using a human-readable markup language rather than visually using Interface Builder, similar to how applications are built for Android and .NET.

For example, the following markup creates an instance of UILabel and sets the value of its text property to "Hello, World!":

<UILabel text="Hello, World!"/>

The output produced by this markup is identical to the output of the following Swift code:

let label = UILabel()
label.text = "Hello, World!"

This article introduces the MarkupKit framework and provides an overview of some of its key features.

Document Structure

MarkupKit uses XML to define the structure of a user interface. The hierarchical nature of an XML document parallels the view hierarchy of an iOS application, making it easy to understand the relationships between views.

Elements

Elements in a MarkupKit document typically represent instances of UIView or its subclasses. As elements are read by the XML parser, the corresponding class instances are dynamically created and added to the view hierarchy.

For example, the following markup declares an instance of LMColumnView containing a UIImageView and a UILabel. LMColumnView is a MarkupKit-provided subclass of UIView that automatically arranges its subviews in a vertical line:

<LMColumnView>
    <UIImageView image="world.png" contentMode="center"/>
    <UILabel text="Hello, World!" textAlignment="center"/>
</LMColumnView>

Elements may not always represent view instances, however. For example, this markup creates an instance of UISegmentedControl, the content of which is defined by a collection of "segment" tags:

<UISegmentedControl>
    <segment title="Small"/>
    <segment title="Medium"/>
    <segment title="Large"/>
    <segment title="Extra-Large"/>
</UISegmentedControl>

Attributes

Attributes in a MarkupKit document typically represent view properties. For example, the following markup declares an instance of a system-style UIButton and sets its title property to "Press Me!":

<UIButton style="systemButton" title="Press Me!"/>

Property values are set using key-value coding (KVC). Type conversions for string, number, and boolean properties are handled automatically by KVC. Other types, such as colors, fonts, images, and enumerations, are handled specifically by MarkupKit.

For example, the following markup creates a label whose font is set to 24-point Helvetica and whose text color is set to "#ff0000", or bright red:

<UILabel text="A Red Label" font="Helvetica 24" textColor="#ff0000"/>

A few attributes have special meaning in MarkupKit and do not represent properties. These include "style", "class", and "id". Their respective purposes are explained in more detail later.

Additionally, attributes whose names begin with "on" represent control events, or "actions". The values of these attributes represent the handler methods that are triggered when their associated events are fired. For example, this markup creates a button with an associated action that will be triggered when the button is pressed:

<UIButton style="systemButton" title="Press Me!" onPrimaryActionTriggered="buttonPressed"/>

Actions are also discussed in more detail below.

Template Properties

Often, when constructing a user interface, the same set of property values are applied repeatedly to instances of a given type. For example, an application designer may want all buttons to have a similar appearance. While it is possible to simply duplicate the property definitions across each button instance, this is repetitive and does not allow the design to be easily modified later – every instance must be located and modified individually, which can be time consuming and error prone.

MarkupKit allows developers to abstract common sets of property definitions into CSS-like "property templates", which can then be applied by name to individual view instances. This makes it much easier to assign common property values as well as modify them later.

Property templates are specified using JavaScript Object Notation (JSON), and may be either external or inline. Inline templates are defined within the markup document itself, and external templates are specified in a separate file.

For example, the following JSON document defines a template named "greeting", which contains definitions for "font" and "textAlignment" properties:

{
  "greeting": {
    "font": "Helvetica 24", 
    "textAlignment": "center"
  }
}

Templates are added to a MarkupKit document using the properties processing instruction (PI). The following PI adds all properties defined by MyStyles.json to the current document:

<?properties MyStyles?>

Inline templates simply embed the entire template definition within the processing instruction:

<?properties {
  "greeting": {
    "font": "Helvetica 24", 
    "textAlignment": "center"
  }
}?>

Templates are applied to view instances using the reserved "class" attribute. The value of this attribute refers to the name of a template defined within the current document. All property values defined by the template are applied to the view. Nested properties, such as "titleLabel.font", are supported.

For example, given the preceding template definition, the following markup would produce a label reading "Hello, World!" in 24-point Helvetica with horizontally centered text:

<UILabel class="greeting" text="Hello, World!"/>

Multiple templates can be applied to a view using a comma-separated list of template names; for example:

<UILabel class="bold, red" text="Bold Red Label"/>

Outlets

The reserved "id" attribute can be used to assign a name to a view instance. This creates an "outlet" for the view that makes it accessible to calling code. Using KVC, MarkupKit "injects" the named view instance into the document's owner (generally either the view controller for the root view or the root view itself), allowing the application to interact with it.

For example, the following markup declares an instance of UITextField and assigns it an ID of "textField":

<UITextField id="textField"/>

The owning class might declare an outlet for the text field in Objective-C like this:

@property (nonatomic) IBOutlet UITextField *textField;

or in Swift, like this:

@IBOutlet var textField: UITextField!

In either case, when the document is loaded, the outlet will be populated with the text field instance, and the application can interact with it just as if it was defined in a storyboard or created programmatically.

Actions

Most non-trivial applications need to respond in some way to user interaction. UIKit controls (subclasses of the UIControl class) fire events that notify an application when such interaction has occurred. For example, the UIButton class fires the UIControlEventPrimaryActionTriggered event when a button instance is tapped.

While it would be possible for an application to register for events programmatically using outlets, MarkupKit provides a more convenient alternative. Any attribute whose name begins with "on" (but does not refer to a property) is considered a control event. The value of the attribute represents the name of the action that will be triggered when the event is fired.

For example, the following markup declares an instance of UIButton that calls the buttonPressed: method of the document's owner when the button is tapped:

<UIButton style="systemButton" title="Press Me!" onPrimaryActionTriggered="buttonPressed:"/>

For example:

@IBAction func buttonPressed(_ sender: UIButton) {
    // Handle button press
}

Localization

If an attribute's value begins with "@", MarkupKit attempts to look up a localized version of the value before setting the property. For example, if an application has defined a localized greeting in Localizable.strings as follows:

"hello" = "Hello, World!";

the following markup will produce an instance of UILabel with the value of its text property set to "Hello, World!":

<UILabel text="@hello"/>

If a localized value is not found, the key will be used instead. For example, if the application does not provide a localized value for "goodbye", the following markup will produce a label containing the literal text "goodbye":

<UILabel text="@goodbye"/>

This allows developers to easily identify missing string resources at runtime.

MarkupKit Classes

MarkupKit includes a number of classes to help simplify application development. Some of the most common are discussed below.

LMViewBuilder

LMViewBuilder is the class that is actually responsible for loading a MarkupKit document. It provides the following class method, which, given a document name, owner, and optional root view, deserializes a view hierarchy from markup:

+ (UIView *)viewWithName:(NSString *)name owner:(nullable id)owner root:(nullable UIView *)root;

The name parameter represents the name of the view to load. It is the file name of the XML document containing the view declaration, minus the .xml extension.

The owner parameter represents the view's owner. It is often an instance of UIViewController, but this is not strictly required. For example, custom table and collection view cell classes often specify themselves as the owner.

The root parameter represents the value that will be used as the root view instance when the document is loaded. This value is often nil, meaning that the root view will be specified by the document itself. However, when non-nil, it means that the root view is being provided by the caller.

For example, a view controller that is defined by a storyboard already has an established view instance when viewDidLoad is called. The controller can pass itself as the view's owner and the value of its view property as the root argument. This allows the navigational structure of the application (i.e. segues) to be defined in a storyboard, but the content of individual views to be defined in markup.

Layout Views

Auto layout is an iOS feature that allows developers to create applications that automatically adapt to device size, orientation, or content changes. An application built using auto layout generally has little or no hard-coded view positioning logic, but instead dynamically arranges user interface elements based on their preferred or "intrinsic" content sizes.

Auto layout in iOS is implemented primarily via layout constraints, which, while powerful, are not particularly convenient to work with. To simplify the process, MarkupKit provides the following set of view classes, whose sole responsibility is managing the size and position of their respective subviews:

  • LMRowView – arranges subviews in a horizontal line
  • LMColumnView – arranges subviews in a vertical line
  • LMLayerView – arranges subviews in layers, like a stack of transparencies

These classes use layout constraints internally, allowing developers to easily take advantage of auto layout while eliminating the need to manage constraints directly.

For example, the periodic table shown below was constructed using a combination of row views, column views, and labels:

Creating this view in Interface Builder would be an arduous task. Creating it programmatically would be even more difficult. However, in markup it is almost trivial. The complete source code for this example can be found here.

LMRowView

The LMRowView class arranges its subviews in a horizontal line. Subviews are laid out from leading to trailing edge in the order in which they are declared. For example, the following markup creates a row view containing three labels:

<LMRowView layoutMargins="12">
    <UILabel text="One"/>
    <UILabel text="Two"/>
    <UILabel text="Three"/>
    <LMSpacer/>
</LMRowView>

The "layoutMargins" attribute establishes a 12-pixel wide gap around the row view's border, and the trailing spacer view ensures that the labels are left-aligned within the row (or right-aligned in locales that use right-to-left text):

Spacer views are discussed in more detail later.

Baseline Alignment

Subviews can be baseline-aligned within a row using the alignToBaseline property. For example, this markup creates a row view containing three labels, all with different font sizes:

<LMRowView alignToBaseline="true" layoutMargins="12">
    <UILabel text="Ten" font="Helvetica 12"/>
    <UILabel text="Twenty" font="Helvetica 24"/>
    <UILabel text="Thirty" font="Helvetica 48"/>
    <LMSpacer/>
</LMRowView>

Because alignToBaseline is set to true, the baselines of all three labels will line up:

LMColumnView

The LMColumnView class arranges its subviews in a vertical line. Subviews are laid out from top to bottom in the order in which they are declared. For example, the following markup creates a column view containing three text fields:

<LMColumnView layoutMargins="12">
    <UITextField placeholder="First" borderStyle="roundedRect"/>
    <UITextField placeholder="Second" borderStyle="roundedRect"/>
    <UITextField placeholder="Third" borderStyle="roundedRect"/>
    <LMSpacer/>
</LMColumnView>

The left and right edges of each subview are automatically pinned to the left and right edges of the column view, ensuring that all of the text fields are the same width:

Grid Alignment

Nested subviews of a column view can be vertically aligned in a spreadsheet-like grid using the alignToGrid property. When this property is set to true, cells in contiguous rows will be resized to match the width of the widest cell in the column.

For example, the following markup would produce a grid containing three rows and two columns:

<LMColumnView alignToGrid="true" layoutMargins="12">
    <LMRowView>
        <UILabel text="One"/>
        <UITextField weight="1" placeholder="First" borderStyle="roundedRect"/>
    </LMRowView>

    <LMRowView>
        <UILabel text="Two"/>
        <UITextField weight="1" placeholder="Second" borderStyle="roundedRect"/>
    </LMRowView>

    <LMRowView>
        <UILabel text="Three"/>
        <UITextField weight="1" placeholder="Third" borderStyle="roundedRect"/>
    </LMRowView>
</LMColumnView>

The weight values ensure that the text fields are allocated all of the remaining space within each row after the size of label has been determined:

Weights are discussed in more detail below.

View Weights

Often, a row or column view will be given more space than it needs to accommodate the intrinsic sizes of its subviews. MarkupKit adds a weight property to UIView that is used to determine how the extra space should be allocated. Weight is a numeric value that specifies the amount of excess space the view would like to be given within its superview (once the sizes of all unweighted views have been determined) and is relative to all other weights specified within the superview.

For row views, weight applies to the excess horizontal space, and for column views to the excess vertical space. For example, since both labels in the following example have a weight of 0.5, they will each be allocated 50% of the width of the row view. The labels are given a border to make their bounds more obvious:

<LMRowView layoutMargins="12">
    <UILabel weight="0.5" text="50%" textAlignment="center"
        layer.borderWidth="0.5" layer.borderColor="#ff6666"/>
    <UILabel weight="0.5" text="50%" textAlignment="center"
        layer.borderWidth="0.5" layer.borderColor="#ff6666"/>
</LMRowView>

In this example, the first label will be given one-sixth of the available space, the second one-third (2/6), and the third one-half (3/6):

<LMColumnView layoutMargins="12">
    <UILabel weight="1" text="1/6" textAlignment="center"
        layer.borderWidth="0.5" layer.borderColor="#ff6666"/>
    <UILabel weight="2" text="1/3" textAlignment="center"
        layer.borderWidth="0.5" layer.borderColor="#ff6666"/>
    <UILabel weight="3" text="1/2" textAlignment="center"
        layer.borderWidth="0.5" layer.borderColor="#ff6666"/>
</LMColumnView>

Spacer Views

A common use for weights is to create flexible space around a view. For example, the following markup will center a label horizontally within a row:

<LMRowViewn layoutMargins="12">
    <UIView weight="1"/>
    <UILabel text="Hello, World!"/>
    <UIView weight="1"/>
</LMRowView>

Because such "spacer" views are so common, MarkupKit provides a dedicated UIView subclass called LMSpacer for conveniently creating flexible space between other views. LMSpacer has a default weight of 1, so the previous example could be rewritten as follows, eliminating the "weight" attribute and improving readability:

<LMRowView layoutMargins="12">
    <LMSpacer/>
    <UILabel text="Hello, World!"/>
    <LMSpacer/>
</LMRowView>

Layer Views

The LMLayerView class simply arranges its subviews in layers, like a stack of transparencies. The subviews are all automatically sized to fill the layer view.

For example, the following markup creates a layer view containing an image view and a label:

<LMLayerView>
    <UIImageView image="world.png" contentMode="center"/>
    <UILabel text="Hello, World!" textColor="#ffffff" textAlignment="center"/>
</LMLayerView>

Since it is declared first, the contents of the image view will appear beneath the label text:

More Information

This article introduced the MarkupKit framework and provided an overview of some of its key features, including auto layout, property templates, and localization.

The latest MarkupKit release can be downloaded here. It is also available via CocoaPods.

For more information, see the project README.

MarkupKit 2.3 Released

MarkupKit 2.3 is now available for download. This release adds support for includes, which allow content defined in another markup document to be “included” or “imported” into the current document. They enable the structure of a user interface to be modularized; for example, to break up a complex document into smaller, more manageable pieces.

Includes are specified using the include processing instruction. The PI’s data value represents the name of the document to include. For example, the following processing instruction would import a document named FirstRow.xml into the current document:

<LMColumnView>
    <?include FirstRow?>
    ...
</LMColumnView>

The content of the included document is handled the same as if it had been defined inline within the parent document. The include’s root view is appended to the view instance containing the PI, and all outlets and actions defined by the include are assigned to the parent’s owner. Property templates defined by the parent document are visible to includes, but templates defined by an include are only visible to the include itself (as well as to any nested includes).

Additionally, all code samples have been updated to work with Xcode 8 and Swift 3. For more information, see the project README.

Creating a Simple Java REST Service Using HTTP-RPC

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&amp;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.

Creating a Simple iOS REST Client Using HTTP-RPC

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.

Although it is, of course, possible to use HTTP-RPC's client libraries to invoke web services implemented using the server-side framework, this isn't strictly required. The client APIs can actually be used to access any JSON-based REST service, regardless of server platform.

This article provides a demonstration of how the HTTP-RPC iOS client library can be used to invoke services provided by JSONPlaceholder, a "fake online REST API".

Service API

JSONPlaceholder offers a collection of web services that simulate common REST operations on a variety of resource types such as "albums", "photos", and "users". For example, the following URL retrieves a JSON document containing a list of simulated user records:

https://jsonplaceholder.typicode.com/users

The document is similar to the following:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  ...
]

Additionally, the service provides a collection of simulated discussion posts, which can be retrieved on a per-user basis as follows:

https://jsonplaceholder.typicode.com/posts?userId=1

For example:

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  ...
]

Sample Application

The sample application presents two views. The first one displays a list of users:

The second displays a list of posts by the selected user:

WSWebServiceProxy Class

The WSWebServiceProxy class is used to invoke service operations. Internally, this class uses an instance of NSURLSession to issue HTTP requests. NSJSONSerialization is used to decode the response data.

Service methods are executed by calling invoke:path:arguments:resultHandler:. This method takes the following arguments:

  • method – the HTTP method to execute (e.g. "GET", "POST")
  • path – the resource path
  • arguments – a dictionary containing the request arguments as key/value pairs
  • resultHandler – a callback that will be invoked upon completion of the method

A convenience method is also provided for executing operations that don't take any arguments.

Arguments are passed to the service either via the query string or in the request body, like an HTML form. Array arguments represent multi-value parameters and are handled similarly to <select multiple> tags in HTML.

The result handler is a callback that is invoked upon completion of the request. If the operation completes successfully, the first argument will contain the result of the operation. If the operation fails, the second argument will be populated with an instance of NSError describing the error that occurred.

For example, the following code might be used to invoke an operation that returns the sum of two numbers, specified by the "a" and "b" arguments. The service would return the value 6 in response:

// Get sum of "a" and "b"
serviceProxy.invoke("GET", path: "/math/sum", arguments: ["a": 2, "b": 4]) {(result, error) in
    // result is 6
}

Application Delegate

An instance of WSWebServiceProxy is created by the application delegate at startup:

fileprivate(set) static var serviceProxy: WSWebServiceProxy!

var window: UIWindow?

func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    AppDelegate.serviceProxy = WSWebServiceProxy(session: URLSession.shared, serverURL: URL(string: "https://jsonplaceholder.typicode.com")!)

    return true
}

The user and post view controllers discussed below use this proxy to invoke their respective service operations.

UserViewController Class

The UserViewController class is responsible for presenting the list of users returned from /users. Internally, it maintains an array of objects representing the user list. In viewWillAppear:, if the list has not already been loaded, it uses the service proxy to retrieve the user list from the server. An activity indicator is shown while the method is executing, and hidden once the request has completed:

class UserViewController: UITableViewController {
    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)

    var users: [[String: AnyObject]]! = nil

    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if (users == nil) {
            tableView.separatorStyle = UITableViewCellSeparatorStyle.none
            activityIndicatorView.startAnimating()

            AppDelegate.serviceProxy.invoke("GET", path: "/users") {(result, error) in
                self.tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
                self.activityIndicatorView.stopAnimating()

                if (error == nil) {
                    self.users = result as! [[String: AnyObject]]

                    self.tableView.reloadData()
                } else {
                    NSLog(error!.localizedDescription)
                }
            }
        }
    }

    ...
}

A custom cell class is used to present the user details for each row:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (users == nil) ? 0 : users.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let user = users[(indexPath as NSIndexPath).row]

    let cell = tableView.dequeueReusableCell(withIdentifier: UserCell.self.description()) as! UserCell

    cell.nameLabel.text = user["name"] as? String
    cell.emailLabel.text = user["email"] as? String

    return cell
}

Finally, when a user is selected, an instance of PostViewController is created and pushed onto the navigation stack:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let postViewController = PostViewController()

    postViewController.userID = users[(indexPath as NSIndexPath).row]["id"] as! Int

    navigationController?.pushViewController(postViewController, animated: true)
}

PostViewController Class

The PostViewController is responsible for presenting the list of user posts returned from /posts. Like UserViewController, it maintains an array of object representing the server response, and populates the array in viewWillAppear::

class PostViewController: UITableViewController {
    var userID: Int!

    let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)

    var posts: [[String: AnyObject]]! = nil

    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if (posts == nil) {
            tableView.separatorStyle = UITableViewCellSeparatorStyle.none
            activityIndicatorView.startAnimating()

            AppDelegate.serviceProxy.invoke("GET", path: "/posts", arguments: ["userId": userID]) {(result, error) in
                self.tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
                self.activityIndicatorView.stopAnimating()

                if (error == nil) {
                    self.posts = result as! [[String: AnyObject]]

                    self.tableView.reloadData()
                } else {
                    NSLog(error!.localizedDescription)
                }
            }
        }
    }

    ...
}

Again, a custom cell class is used to present the details for each row:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (posts == nil) ? 0 : posts.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let post = posts[(indexPath as NSIndexPath).row]

    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.description()) as! PostCell

    cell.titleLabel.text = post["title"] as? String
    cell.bodyLabel.text = post["body"] as? String

    return cell
}

More Information

This article provided a demonstration of how the HTTP-RPC iOS client library can be used to build a simple REST client application. The complete source code for the sample application can be found here.

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

Efficiently Transforming JDBC Query Results to JSON

A lot of enterprise data is stored in relational databases and accessed via SQL queries. Many web services are little more than HTTP-based wrappers around such queries.

Unfortunately, transforming query results to JSON so it can be consumed by a client application often involves numerous inefficient steps, such as binding each row to a data object and loading the entire data set into memory before serializing it back to the caller. This type of approach has a negative impact on performance and scalabilty. Each row requires multiple heap allocations and constructor invocations, increasing latency and CPU load. Worse, the caller does not receive a response until the entire data set has been processed.

Further, since each response is loaded entirely into memory, high-volume applications require a large amount of RAM, and can only scale through the addition of more physical hardware. Eventually, the garbage collector has to run, slowing down the entire system.

A much more efficient approach is to stream response data. Instead of copying the query results into an in-memory data structure before sending the response, the web service can write a row of data to the output stream each time a row is read from the result set. This allows a client to begin receiving the data as soon as it is available, significantly reducing latency. Also, because no intermediate data structures are created, CPU and memory load is reduced, allowing each server to handle a higher number of concurrent requests. Finally, because fewer heap allocations are required, the garbage collector needs to run much less frequently, resulting in fewer system pauses.

Introducing HTTP-RPC

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&amp;b=4

The service would return the value 6 in response.

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.

ResultSetAdapter Class

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.

For example, consider a web service that returns the result of a SQL query on this 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)
);

The service can simply execute the query, pass the result set to the adapter’s constructor, and return the adapter instance:

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

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

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

With just a few lines of code, the query results are quickly and efficiently returned to the caller, with the framework ensuring that the underlying database resources are properly disposed of once the response has been sent.

More Information

This article introduced the HTTP-RPC framework and provided an example of how the ResultSetAdapter class can be used to efficiently transform JDBC query results into JSON.

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

Regular Expression to Validate a Comma-Separated List of Email Addresses

Recently, I needed to create a regular expression to validate the format of a comma-separated list of email addresses. Just thought I’d share the result in case it is of use to anyone. The pattern I came up with for matching a single address is as follows:

[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+

Note that I didn’t attempt to handle every possible combination of valid characters – only what I’d consider the most common ones.

The pattern is simply duplicated for the comma-separated list. Here’s an example of applying the pattern in Java:

Pattern addressListPattern = Pattern.compile(String.format("%1$s(,\\s*%1$s)*",
    "[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+"),
    Pattern.UNICODE_CHARACTER_CLASS);

System.out.println(addressListPattern.matcher("xyz").matches()); // false
System.out.println(addressListPattern.matcher("foo@bar.com").matches()); // true
System.out.println(addressListPattern.matcher("foo@bar.com, xyz").matches()); // false
System.out.println(addressListPattern.matcher("foo@b-ar.com, f.oo@b-ar.co.uk").matches()); // true
System.out.println(addressListPattern.matcher("føo@b-ar.com, f-oo@b-ar.co.uk").matches()); // true

Note that Pattern.UNICODE_CHARACTER_CLASS is not necessary (or supported) in Android, which supports Unicode character matching by default.

Hope it helps!