Gateways

Interfacing with fjåge from other languages

While fjåge agents are developed in Java or Groovy, applications written in other languages can interact with them through a Gateway. A gateway connects to a fjåge master container over a TCP/IP (or WebSocket) connection, and lets the application send and receive messages, look up agents providing specific services, and subscribe to topics — just as an agent would. Under the hood, all gateways speak the same JSON protocol, so a gateway can be developed for any language with a network stack and a JSON library.

From the master container’s perspective, a gateway appears as a slave container holding a single gateway agent that proxies messages on behalf of the connected application.

The proxy agent created by a gateway is named with the reserved gateway- prefix (e.g. gateway-5f3a); non-gateway agents should not use this prefix. The master container uses this naming convention to identify gateway connections and keep them lightweight: once a connection is classified as a gateway, the master stops sending it directory queries (agent lists, service lookups, etc.), and answers such queries from its cached knowledge of the gateway agent instead. For the same reason, gateway agents cannot register services — a service registered on a gateway agent would not be discoverable by other agents. If a gateway connects with an agent name that collides with an existing agent, the master shuts down that gateway connection and logs a warning.

Supported languages

Language Guide API documentation Package
Java / Groovy this chapter (below) javadoc GitHub Packages
Python 3 Python Gateway fjagepy PyPI (pip install fjagepy)
JavaScript JavaScript Gateway fjage.js npm (npm install fjage)
C C Gateway fjage.h build from gateways/c
Julia Julia Gateway Fjage.jl Julia registry (pkg> add Fjage)

The Python, JavaScript and C gateways live in the gateways/ folder of the fjåge repository; the Julia gateway is maintained separately as Fjage.jl. The formal specification that all gateway implementations follow is in Gateways.md.

The Java/Groovy gateway

Java and Groovy applications that are not themselves fjåge containers can use the org.arl.fjage.remote.Gateway class directly:

import org.arl.fjage.*
import org.arl.fjage.remote.Gateway

def gw = new Gateway('localhost', 1100)
def shell = gw.agentForService(org.arl.fjage.shell.Services.SHELL)
gw.send(new Message(shell, Performative.REQUEST))
gw.close()

The Gateway class supports the same messaging methods as an agent (send(), receive(), request(), subscribe(), agentForService(), etc.), described in the Messaging chapter.

A common example

All gateways follow the same pattern: open a connection to the master container, exchange messages, and close. The per-language chapters develop this in detail, but the skeleton looks alike everywhere:

from fjagepy import Gateway, MessageClass

ShellExecReq = MessageClass('org.arl.fjage.shell.ShellExecReq')
gw = Gateway('localhost', 1100)
shell = gw.agentForService('org.arl.fjage.shell.Services.SHELL')
rsp = gw.request(ShellExecReq(recipient=shell, command='ps'))
print(rsp.performative)
gw.close()
import { Gateway, MessageClass } from 'fjage';

const ShellExecReq = MessageClass('org.arl.fjage.shell.ShellExecReq');
const gw = new Gateway({ hostname: 'localhost', port: 1100 });
const shell = await gw.agentForService('org.arl.fjage.shell.Services.SHELL');
const rsp = await gw.request(new ShellExecReq({ recipient: shell, command: 'ps' }));
console.log(rsp.perf);
gw.close();
using Fjage

gw = Gateway("localhost", 1100)
shell = agentforservice(gw, "org.arl.fjage.shell.Services.SHELL")
rsp = request(gw, ShellExecReq(recipient=shell, command="ps"))
println(rsp.performative)
close(gw)
#include "fjage.h"

fjage_gw_t gw = fjage_tcp_open("localhost", 1100);
fjage_aid_t shell = fjage_agent_for_service(gw, "org.arl.fjage.shell.Services.SHELL");
fjage_msg_t req = fjage_msg_create("org.arl.fjage.shell.ShellExecReq", FJAGE_REQUEST);
fjage_msg_set_recipient(req, shell);
fjage_msg_add_string(req, "command", "ps");
fjage_msg_t rsp = fjage_request(gw, req, 1000);
if (rsp != NULL) fjage_msg_destroy(rsp);
fjage_aid_destroy(shell);
fjage_close(gw);

To try any of these, start a fjåge master container with a TCP port open (see Distributed Agents):

import org.arl.fjage.*
import org.arl.fjage.remote.MasterContainer

def platform = new RealTimePlatform()
def container = new MasterContainer(platform, 1100)
platform.start()