2.4. Hello, Agent!

Having completed the setup, we’re ready to expand our tutorial application. We’ll now incorporate SwimOS’s fundamental building blocks - agents. Agents will be thoroughly covered in a dedicated chapter. To help you start using agents, we’ll bypass certain details until that chapter.

Agent Class

Agents are like regular Java objects with a few additional features built in. Let’s create one by extending SwimOS’s AbstractAgent:

File: src/main/java/tutorial/Agent.java

import swim.api.agent.AbstractAgent;

public class Agent extends AbstractAgent {

  @Override
  public void didStart() {
    System.out.println(nodeUri() + ": Hello, agent!");
  }

}

Just like the plane, agents have their own lifecycle callbacks, which we’ve used to print a log line on startup. The nodeUri() method allows an agent to access its own URI, let’s print that at the start of the line.

With a basic agent class defined, we can modify our space definition in the server config file to create the agent with a given URI.

File: src/main/resources/server.recon

tutorial: @fabric {
    @plane(class: "tutorial.MainPlane")

    @node {
        uri: "/Bond"
        @agent(class: "tutorial.Agent")
    }

}

Let’s give this a run:

$ ./gradlew run

> Task :run
Starting server...
Hello, world!
Running server...
/Bond: Hello, agent!
<=========----> 75% EXECUTING [1s]

You can see that we have an additional log line, from our agent with the URI /Bond. Notice how we didn’t have to explicitly create the object, we just included it in the config file and it started.

Multiple Agent Pattern

Let’s go one step further and modify the server config file slightly. We are going to change the static URI to a URI pattern:

File: src/main/resources/server.recon

tutorial: @fabric {
    @plane(class: "tutorial.MainPlane")

    @node {
        pattern: "/:name"
        @agent(class: "tutorial.Agent")
    }

}

If you were to run this, you would notice that we have lost the log line from /Bond.

Now the agent has a URI pattern, the server will not create any on startup. Instead, an instance of the agent will be created when a command is directed to a URI that matches the pattern.

Commands are messages sent to agents - they can be sent from a variety of places both inside and outside the application. Conveniently, the plane has the ability to command agents, which we already have a lifecycle callback for. Modifying the didStart callback on the plane, we command two agents with different URIs:

File: src/main/java/tutorial/MainPlane.java

  @Override
  public void didStart() {
    System.out.println("Hello, world!");
    command("/Bond", "", Value.absent());
    command("/Holmes", "", Value.absent());
  }

Commands are usually directed to the lane of an agent and can contain a payload, hence the two empty placeholder arguments.

Let’s give our tutorial application one final run to see our agents get created:

$ ./gradlew run

> Task :run
Starting server...
Hello, world!
/Bond: Hello, agent!
/Holmes: Hello, agent!
Running server...
<=========----> 75% EXECUTING [1s]

You can see that sending a message to a URI that matches the pattern we defined before causes an agent with that URI to be created.

We haven’t yet covered a lot of the features that make agents useful for building streaming applications, but we have already seen how easy they are to create. On the next page we will continue this example to add lanes to these agents.