HTTP Lanes

In previous guides, we have seen how Swim applications consist of interconnected Web Agents with properties and methods in the form of Lanes.

HTTP Lanes accept HTTP requests and respond enabling Web Agents to expose endpoints. This allows Web Agent’s state to be viewed or modified from outside of Swim applications through the use of REST APIs.

Declaration

All lanes are declared inside Web Agents as fields annotated with @SwimLane. The parameter inside this annotation is the lane’s laneUri. Recall that every Web Agent has a universal, logical address known as its nodeUri; laneUris are simply the equivalent counterpart for lanes.

The following declaration is sufficient to make the http lane of every UnitAgent addressable by the laneUri "http":

// swim/basic/UnitAgent.java
package swim.basic;

import swim.api.SwimLane;
import swim.api.agent.AbstractAgent;
import swim.api.http.HttpLane;
import swim.structure.Value;

public class UnitAgent extends AbstractAgent {
  @SwimLane("http")
  HttpLane<Value> http;
}

Instantiation

The AbstractAgent class comes with utility methods to construct runnable lanes. Recall, however, that developers rarely instantiate Web Agents by explicitly invoking their constructors. The recommended pattern for adding a HTTP lane to an Agent is instead to:

// swim/basic/UnitAgent.java
package swim.basic;

import swim.api.SwimLane;
import swim.api.agent.AbstractAgent;
import swim.api.http.HttpLane;
import swim.http.HttpResponse;
import swim.http.HttpStatus;
import swim.structure.Value;

public class UnitAgent extends AbstractAgent {
  @SwimLane("http")
  HttpLane<Value> http = this.<Value>httpLane()
      .doRespond(request -> {
        return HttpResponse.from(HttpStatus.OK).body("Hello World");
      });
}

The http lane in the example above, when running on localhost:9001 and agent has nodeUri /unit, can be addressed with: localhost:9001/unit?lane=http. A HTTP response with body of Hello World will be returned to every request.

HTTP Methods

The HttpRequest object, passed to the callback method of the lane, has access to the HTTP method of the request with method(), we can use this to determine the operation required. Here we add the ability to alter the state of an agent by using a POST request:

// swim/basic/UnitAgent.java
package swim.basic;

import swim.api.SwimLane;
import swim.api.agent.AbstractAgent;
import swim.api.http.HttpLane;
import swim.api.lane.ValueLane;
import swim.http.HttpMethod;
import swim.http.HttpResponse;
import swim.http.HttpStatus;
import swim.http.MediaType;
import swim.recon.Recon;
import swim.structure.Value;

public class UnitAgent extends AbstractAgent {

  @SwimLane("state")
  ValueLane<Value> state = this.<Value>valueLane()
      .didSet((newValue, oldValue) -> {
        System.out.println("State changed from " + Recon.toString(oldValue) + " to " + Recon.toString(newValue));
      });

  @SwimLane("http")
  HttpLane<Value> http = this.<Value>httpLane()
      .doRespond(request -> {
         **if (HttpMethod.POST.equals(request.method())) state.set(request.entity().get());**
          return HttpResponse.from(HttpStatus.OK).body(Recon.toString(state.get()), MediaType.applicationXRecon());
        });
}

Content Type

As well as Recon, HTTP Lanes also allow for different content types to be returned in the response body such as Json. Simply add the media type when setting the body of the HTTP response.

Here we create a new lane in our previous example to return a Json response:

// swim/basic/UnitAgent.java
  @SwimLane("httpJson")
  HttpLane<Value> httpJson = this.<Value>httpLane()
      .doRespond(request ->
        HttpResponse.from(HttpStatus.OK).body(Json.toString(state.get()), MediaType.applicationJson()));

Try It Yourself

A standalone project that combines all of these snippets and handles any remaining boilerplate is available here.