Collections and an HTTP client agent
These examples adapt the passive mams-hello and mams-helloagent projects from mams-guide. They illustrate the same HTTP interface being used by a human-operated client and an ASTRA agent.
Publish a passive collection
Download the collection project, extract it and run mvn clean compile astra:deploy from its directory.
The agent creates a collection at /main/hello, using Hello.java to describe each child’s fields:
agent Main extends mams.javatypes.Passive {
plan +!main(list args) {
MAMSAgent::!setup();
MAMSAgent::!created("base");
Passive::!listResource("hello", "Hello");
}
}
public class Hello {
public String name;
}
Unlike the individual item in the first example, this hello resource is a collection. POST creates a child; PUT applies to that child, rather than to the collection.
Create a greeting:
curl -i -X POST \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'Slug: rem' \
-d '{"name":"Rem"}' \
http://localhost:9000/main/hello
Expect 201 Created and a Location header identifying /main/hello/rem. Use the returned URI for subsequent requests. Read the item, replace its name, then delete it:
curl -i -H 'Accept: application/json' \
http://localhost:9000/main/hello/rem
curl -i -X PUT \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"name":"Bob"}' \
http://localhost:9000/main/hello/rem
curl -i -X DELETE -H 'Accept: application/json' \
http://localhost:9000/main/hello/rem
These successful operations return 200 OK. GET after PUT shows {"name":"Bob"}. GET after deletion returns 404 Not Found. GET on /main/hello retrieves the collection representation. The project’s test.http provides requests you can run in an HTTP client.
Run an agent as an HTTP client
Leave the collection server running. Download the client project, extract it and run mvn clean compile astra:deploy in another terminal.
The client creates its own MAMS infrastructure on port 9001, then performs this sequence against the collection server:
Read the collection.
POST a greeting for Rem, requesting the child name
rem.Read the new item using the response’s
Locationheader.PUT a greeting for Bob at that URI and read it again.
DELETE the item and read the collection again.
If you previously created a child called rem, delete it before running the client. A repeated slug would produce 409 Conflict, and this example treats that as failure.
import com.fasterxml.jackson.databind.JsonNode;
import mams.web.HttpResponse;
agent Main extends mams.MAMSAgent {
constant string RESOURCE_URI = "http://localhost:9000/main/hello";
module mams.JSONBuilder builder;
module Strings strings;
plan +!main(list args) {
mams.MAMSAgent::!setup(9001);
!read(RESOURCE_URI);
!create(RESOURCE_URI, "Rem", "rem", string uri);
!read(uri);
!update(uri, "Bob");
!read(uri);
!delete(uri);
!read(RESOURCE_URI);
S.exit();
}
plan +!create(string target, string name, string slug, string uri) {
JsonNode node = builder.createObject();
builder.addProperty(node, "name", name);
mams.MAMSAgent::!httpRequest(
httpUtils.post(target, [header("Slug", slug)], builder.toJsonString(node)),
HttpResponse response
);
if (httpUtils.hasCode(response, 201)) {
// This tutorial server is local; preserve the returned path.
uri = strings.replaceAll(httpUtils.header(response, "Location"),
"^https?://[^/]+", "http://localhost:9000");
} else {
S.fail();
}
}
plan +!read(string uri) {
mams.MAMSAgent::!get(uri, HttpResponse response);
if (httpUtils.hasCode(response, 200)) {
string content = httpUtils.bodyAsString(response);
console.println("GET: " + uri);
console.println(content);
} else {
S.fail();
}
}
plan +!update(string target, string name) {
JsonNode node = builder.createObject();
builder.addProperty(node, "name", name);
mams.MAMSAgent::!put(
target, builder.toJsonString(node),
HttpResponse response
);
if (~httpUtils.hasCode(response, 200)) {
S.fail();
}
}
plan +!delete(string target) {
mams.MAMSAgent::!delete(target, HttpResponse response);
if (~httpUtils.hasCode(response, 200)) {
S.fail();
}
}
}
This local tutorial preserves the path returned in Location but uses its known server address, http://localhost:9000. MAMS generates absolute resource URIs using the host address resolved by the JVM, which may not be reachable from the client. In a deployed application, use a correctly advertised, reachable service address; do not apply this loopback replacement across hosts.
The mams.JSONBuilder module constructs the request data. httpUtils.post builds a request with a Slug header, while httpRequest executes it. The other requests use the core get, put and delete goals. Each helper checks the HTTP status, and create returns the URI from Location.
The console shows the representations before and after replacement, including the name Bob. The client exits after the sequence. It does not create a resource base of its own: an agent can consume another service without exposing resources.