Chapter 3. Developing an agent

Table of Contents

3.1. Messages
3.1.1. Data structure of Messages
3.1.2. Keys and possible values in Messages
3.1.3. FIPA
3.1.4. Message Delivery
3.1.5. Message Security
3.1.6. External format
3.2. Habitat communication
3.2.1. Architecture
3.2.2. Configuration
3.2.3. Creating a new messenger plugin
3.2.4. The ping tool
3.3. Addresses
3.3.1. Formal address definition
3.3.2. Example addresses
3.3.3. Habitat names
3.3.4. Agent registration
3.3.5. Creating an address
3.3.6. How are addresses resolved?
3.3.7. Equality of addresses
3.4. IDs
3.4.1. The Habitat ID
3.4.2. The Agent ID
3.5. JNDI
3.5.1. JNDI operations
3.5.2. The ADK namespaces
3.5.3. Registering an address
3.5.4. Symbolic names
3.5.5. Resolving a JNDI name
3.5.6. Habitat namespaces
3.5.7. Security constraints
3.6. Addresses, JNDI and agent-IDs
3.6.1. Mapping addresses to JNDI
3.6.2. Resolving an agent's address
3.6.3. Agent ID
3.6.4. Canonical agent address

3.1. Messages

Messages are objects that can be sent to agents, thus establishing communication. This section will describe the data structure of messages, the contents of those data structures and their relationship to FIPA.

The end of this section lists some specifications on messaging in general; this is followed by some notes on messages and security.

From a programmer's point of view there are two kinds of messages: the base interface Message and the implementation of that interface OutgoingMessage. The latter are messages that can be sent, the former defines the interface of classes that contain messages that an agent can receive.

This section will use the term “Message” when the distinction is irrelevant or whenever it is clear from the context which kind of message is targeted.

3.1.1. Data structure of Messages

From the most basic point of view, Messages must consist of four Maps and two AgentURIs. The Maps are used for the content and meta information, while the addresses specify the sender and receiver of the message.

The four maps are explained in the following diagram:

Map nameDescription incoming Message implementations OutgoingMessage
content The actual, conversation-specific contents of the message. YesYes
meta Meta information on the message. This meta information is meant to be understood by every agent. YesYes
broker notice Information supplied by the Message Brokers that took part in delivering the message. Yes No
broker hint Information to be read by the Message Brokers that will take part in delivering the message. NoYes

These maps can be reached through the API with the functions specified in the Javadoc.

As usual, these Maps are used to store key-value pairs. However, the Maps used in ARE Messages have some extra restrictions.

The first restriction is that keys must only be Strings. The Maps must use a case-insensitive comparison on those keys; however, they must be case-preserving.

The second restriction says that values, too, must be Strings. This is enforced for several reasons:

  • Allowing any object means that both the sender and the receiver must have the class information for that object (be it provided by the ARE or the agents themselves). The class information on both sides must be serialization compatible.

  • Agents are not meant to be Java specific. Allowing arbitrary objects to be sent around introduces basic problems with other programming languages.

  • Serialization is a relatively costly operation. Since messaging is the core of the ARE, it should be fast.

Note that the ARE must allow the contents of the message maps to contain XML data.

The sender and receiver part of a message are both AddressURIs. The receiver must be specified by the agent sending the message. The sender must automatically be filled in by the ARE.

Messages have sensible hashCode and equals methods. However, equality of messages is co-dependent on equality on addresses. AddressURI's are not resolved before equality is determined. See Section 3.3.6, “How are addresses resolved?” for rules on address equality.

3.1.2. Keys and possible values in Messages

At this point, only the contents for the meta and content maps have been defined. These contents are explained in Table 3.1, “Keys and values in the Meta map” and Table 3.2, “Keys and values in the Content map”.

Note

These tables mention “Allowed values” and “default values”. The ARE may enforce this in the implementation of Messages, but is not required to do so.

Note also that the contents of the content map are different for every content-type. This specification only coverts the type application/x-tryllian-fipa.

Agents may add other fields to these maps. However, other agents are not required to understand those fields. For the sake of compatibility, it is advised that agents do not add those fields.

Table 3.1. Keys and values in the Meta map

KeyAllowed valuesDescription
content-typeapplication/x-tryllian-fipa

Defines the encoding of the keys and values in the content map as a MIME type[RFC1521].

For the application/x-tryllian-fipa type, the map must contain at least the fields content-language and performative.

reply
never
normal

Defines whether it is allowed to reply to this message. If not specified, the default value is normal.

universal-meaning
not-understood
go-away
slow-down

A set of meanings for messages that are language-independent. The meaning not-understood must be supported by all agents. The others should be supported by all agents.

Table 3.2. Keys and values in the Content map

KeyAllowed valuesDescription
content-languagesimple-tuple

This field specifies the format of all fields but this one and the performative field.

The simple-tuple language uses, as its name implies, tuples to represent the contents of a message. The fields required by the simple-tuple language all start with the tuple- prefix.

performativeAny FIPA performative

This field indicates the FIPA performative for the entire message. Performatives are explained in detail in the next section.

tuple-n Any String

This field indicates the nth argument of the message. The exact contents are defined by the protocol.

The tuple numbering scheme must be zero based.

tuple-sizeA String-encoded positive integer

This field contains the total number of tuples. In other words, if the highest argument is called tuple-n, then the value of this field equals n+1.

3.1.3. FIPA

The Tryllian FIPA dialect was based on the FIPA specs that were available in January 1999. Therefore, the Tryllian dialect deviates somewhat from the current specs.

This section describes the general format of the Tryllian dialect and the mapping to Message keys and values.

3.1.3.1. The Tryllian Dialect

The Tryllian dialect has a very simple[1] format. Message content must adhere to the following EBNF specification.

[1]Message::= Performative , Subject , ArgumentList  
[2]ArgumentList::= Argument , [ ArgumentList ]  
[3]Performative::= agree , cancel , failure , inform , not-understood , query-ref , request , refuse , subscribe  

Subjects can be any String. They describe the general scope of the message. Protocols should be named after their Subject.

There are of course conventions for Subjects. Messages with the performative query-ref or subscribe should have a noun as their Subject. Messages with the performative request should have a verb or verb-phrase as their Subject.

The format of Arguments is completely dependent on the Subject.

For a complete overview of protocols supported by the ARE, please refer to ???.

3.1.3.2. Putting the Tryllian Dialect in Messages

The simple-tuple language was designed to provide a straightforward map between the content and the Map representation. Table 3.3, “” specifies how elements of the content are mapped to the Map.

Table 3.3. 

EBNF nameMap key
Performative performative
Subject tuple-0
Argument tuple-n[a]

[a] Here, n is equal to the one-based index of the Argument . In other words, the nth argument from the ArgumentList is mapped to tuple-n

3.1.4. Message Delivery

In order to provide agents with a sense of certainty, the ARE must meet the following requirements:

In-order delivery

Messages sent from one agent to another must arrive in the same order as they were sent. There is no guarantee on the order of messages sent by different agents.

Reasonable performance

Message delivery (especially delivery to other habitats) is largely dependent on the transport layer. However, the ARE may not impose a performance penalty that has a polynomic complexity or worse.

Guaranteed local delivery

The ARE must guarantee the reliability of messages sent to agents on the same habitat. If the message can not be delivered, the ARE must sent back a failure notice.

Note

This does not mean that the ARE must guarantee that a message will always arrive. There are too many situations with varying conditions to make this a specific requirement.

In particular the ARE may send back failures when an agent is temporarily unavailable. For example, when an agent is suspending or moving, the ARE may report a failure saying the agent does not exist.

When a message to a local agent can not be delivered, the ARE must throw a NoSuchAgentException. When a message to a non-local agent can not be delivered, the ARE must send a failure message of the type DeliveryFailure.

3.1.5. Message Security

This version of the ARE does not specify security on a message level. However, the following assumptions are valid:

  • Local messages can not be eavesdropped by third parties, assuming the Habitat itself is not compromised.

  • The transport layer can have a security model. If this model is strong enough, remote messages can not be eavesdropped either.

  • The ARE must provide the sender address in a message. This means that the sender of local messages is guaranteed, still assuming the Habitat itself is not compromised.

  • If the transport layer has good authentication and a good trust model[2], the sender address of remote messages is also guaranteed.

3.1.6. External format

The external format of messages (i.e. the format of messages as used by messenger plugins) is dependent upon the messenger plugin and may impose additional restrictions on the the possible content of messages. Messenger plugins must however support the sending of XML encoded content.

3.1.6.1. JXTA External Message Format

The JXTA messenger plugin encodes ARE messages in a single single XML document that conforms to the following specification. In this format, the values of the message map are encoded as CDATA sections between <value> tags. enabling the transmission of XML data, and, incidentally, removing the need for escaping the value strings. (Note that the underlying JXTA network layer encodes the external format in the binary JXTA wireformat. If encrypted, secure messaging over JXTA is used each ARE message is broken up in small packets that are encrypted and then encoded in the JXTA wireframe format.)

[4]xml_message::= envelope  
[5]key::=string 
[6]sender::=string 
[7]receiver::=string 
[8]version::=major, '.', minor  
[9]envelope::= xml_decl , x-envelope  
[10]content::= x-content  
[11]xml_decl::='<?xml version="1.0" encoding="utf-8">' 
[12]x_envelope::= '<x-envelope version="' , version , '">' , sender , receiver , meta , broker-hint , broker-requirement , x-content, '</x-envelope>'  
[13]meta::= '<meta>' , valuelist , '</meta>'  
[14]broker-hint::='<broker-hint>' , valuelist , '</broker-hint>' 
[15]broker-requirement::='<broker-requirement>' , valuelist , '</broker-hint>' 
[16]x_content::= '<x-content>' , valuelist broker-requirement , '</x-content>'  
[17]valuelist::= '<valuelist>' , value* , '</valuelist>'  
[18]value::= '<value key="' , key , '">' , <![CDATA[stringdata]]>* , '</value>'  

Note: it is still not possible to use XML (broken or correct, does not matter) as keys; additionally, characters forbidden by the XML spec, such as Unicode 0, are forbidden in the value fields.

3.2. Habitat communication

An important feature of the ARE is communication. The ARE enables the dynamic smart components resident in the habitat to communicate with each other, with other dynamic smart components deployed to other ARE habitats and with any external application. Potentially, the ARE supports all industry-standard protocols, from SOAP to JMS, without limitations: it is possible to deploy a variety of protocol handlers within a single habitat, and to dynamically connect and close new protocol handlers called messenger plugins.

The ARE supports the dynamic deployment of different protocol handlers through a plugin architecture. (See the relevant design document). This section defines the specifications for the plugin architecture, the correct way of configuring the messenger plugins and details the steps one should take to create a new messenger plugin.

3.2.1. Architecture

The central idea behind the messenger plugin architecture is that the habitat should have as little to do with external messaging as possible. All responsibility for queueing, message format translation and error handling must be the responsibility of the messenger plugin.

The messenger plugin must send delivery failure notices back to the sending agent and it should send delivery success notices back to the sending agent when so desired.

In the event that the destination habitat is unknown, the plugin must notify the sending habitat as soon as possible, allowing that habitat to resend the message using another messenger plugin, if possible.

The messenger registry may assume that calls to the messenger pluging are synchronous and does not implement any form of queuing. A plugin should thus immediately return with a failure if the destination is not reachable. (However, the messenger registry may be smarter than that.)

3.2.2. Configuration

There are two possibilities for deploying messenger plugins: either they are installed at startup inside the habitat VM, or they are started up in their own VM and contact the habitat through RMI. (Once the management framework has been defined, it will be possible to install, stop and start messenger plugins inside the habitat VM.)

3.2.2.1. Messenger plugins running inside the habitat VM

The first case is the least complex. Given that the other properties are set correctly (as detailed in Section 7.2.8, “Messenger”, all that is necessary is to declare the messenger plugins to be started with a line like the following in the habitat.properties file:

messenger.registry.startmessengers=com.tryllian.messenger.jxta.JXTAMessengerPlugin
      

If you want two or more plugins to run at the same time, you can append their fully qualified class names to this line, separating the plugins with a semicolon. You can have two or more plugins of the same class, or of a different class.

Because it is now possible to have more than one messenger plugin at the same time, possibly of the same type, it is no longer possible to derive the habitat identity or habitat ID from the combination if IP address and messenger port number. (A habitat can have only one identity.)

So there is another property which the habitat can use to compute an ID if there was none given in the habitat.properties file or the certificate. This property also refers to a TCP/IP port, and the habitat will lock that port to prevent two habitats running on the same machine with the same IP address and portnumber -- i.e. it prevent duplication of identity.

habitat.identification.port=50010
      

The default for this property is 50000.

3.2.2.2. External messenger plugins

Messenger plugins that have a main method can be started standalone. These plugins will contact an RMI registry on the local computer and query the registry for running habitats. The messenger plugin will then make contact with the habitat and register itself as a messenger.

Note that it is currently not possible to have messenger plugins run on another computer than the habitat. There is no technical reason for this limitation, and if necessary, this can be changed. However, the security implications would need looking into.

If it is desirable to run external messenger plugins, but the messenger plugin and the habitat need to be carefully configured.

3.2.2.2.1. Configuring the habitat for cooperation with externally running messenger plugins

First of all, we have to actually allow the messenger registry inside the habitat to let remote plugins register:

messenger.rmi.remote-allowed=true
        

It is not necessary, even though possible, to start a separate RMI registry. The habitat provides its own: the host and port where the RMI registry is started can be changed using the relevant properties, but the defaults are sensible.

3.2.2.2.2. Configuring an externally running messenger plugin

The externally running messenger plugin must be able to find the habitat it needs to connect to in the RMI registry. If you haven't set any properties (qui vide), then the default values will obtain, and everything will work.

From this point on, it's just a matter of starting the messenger plugin in the usual way. Note that messenger plugins may use the smart jar-loader that the habitat uses, but that the current JXTA messenger plugin doesn't. You will need to add all relevant jarfiles to the classpath.

3.2.3. Creating a new messenger plugin

A habitat can use zero or more messenger plugins to transport messages to and from the outside world. This outside world is not necessarily another habitat; it is equally likely that a habitat will want to communicate as directly as possible with another application, a J2EE server or a gui.

Consider the last example: a GUI intended to give a user control over the application formed by Tryllian's dynamic smart components. Previously, the GUI would have been built around the JXTA standalone messenger API. This meant that the GUI necessarily ran in a second JVM and that all communication between GUI and application was transported over JXTA pipes.

With the flexible messenger plugin architecture, it is an easy task to create a GUI that runs either inside the same JVM as the smart component application, or in a second JVM, as deployment considerations demand. In the first case, all GUI commands arrive at the smart components with the speed of local messaging. In the second case, there is an RMI layer between the GUI and the application, but that presents much less latency than JXTA.

The plugin must implement the following interfaces:

  • tryllian.messenger.MessengerPlugin

  • tryllian.messenger.MessengerRecord

The MessengerRegistry implements or provides objects that implement the following interfaces:

  • tryllian.messenger.MessageHandler

  • tryllian.messenger.MetaDataProvider

  • tryllian.messenger.RegistrationHandler

3.2.4. The ping tool

The ping tool is a flexible ping application that can use any messenger plugin to determine whether a habitat is alive and reachable.

3.3. Addresses

Previous releases of the ARE supported the hastcp:// addressing scheme. This scheme proved to be too complex in actual use and has been replaced with a new addressing scheme. Support for old-style addresses (that start with hastcp://) has been dropped, together with support for rooms. New addresses start with agent://, as described in this section, and are resolved using JNDI to agentID's.

3.3.1. Formal address definition

An address is essentially an URI, as defined in the URI Specification. A well-formed address follows the syntax that follows.

Syntax of addresses
[19]Address::= LocationAddress | AgentAddress  
[20]LocationAddress::= Scheme , "://" , Habitat  
[21]AgentAddress::= LocationAddress , Separator , AgentPath , [ Query ]  
[22]Scheme::= "agent"  
[23]Habitat::= { NamePart , "." } * , TopNamePart, [ "." ]  

The habitat part of an address is the name of the addressed habitat. A habitat's name must have at least its last part starting with a letter. This is done in order to maintain compatibility with DNS. Habitat names are therefore treated case-insensitive as well.

[24]AgentPath::= PCharacter + , { Separator , PCharacter + } *  

The agent part of an address is corresponding with the agent's registration on the destination habitat. The agent part is treated case-sensitive. This part is considered to be hierarchical.

Note

The current implementation uses JNDI for the agent registration.

[25]Query::= "?" , QCharacter *  

The query part of an address can be used for special directives, e.g. an explicit marker for a group or multicast address. (Support for the query part is not included in the current release of the ARE.)

[26]NamePart::= AlphaDigit | AlphaDigit , { AlphaDigit | "-" } * , AlphaDigit  
[27]TopNamePart::= Alpha | Alpha , { AlphaDigit | "-" } * , AlphaDigit  
[28]AlphaDigit::= Alpha | Digit  
[29]Alpha::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"  
[30]Digit::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"  
[31]Separator::= "/"  
[32]PCharacter::= Unreserved | Escaped | Special | ";"  
[33]QCharacter::= Reserved | Unreserved | Escaped  
[34]Unreserved::= Alpha | Digit | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"  
[35]Escaped::= "%" , Hex , Hex  

Any character that is not allowed as a regular character, can be included in an agent part of the address. This can be done by escaping each octet of its UTF-8 representation. An octet is escaped by using the two digits of its hexadecimal representation, preceded with a "%".

[36]Special::= ":" | "@" | "&" | "=" | "+" | "$" | ","  
[37]Reserved::= ";" | "/" | "?" | Special  
[38]Hex::= Digit | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f"  

3.3.1.1. Deviations from URI

The syntax of addresses presented above form a subset of the standard URI, as defined in the aforementioned RFC 2396. The following is a list of components of an URI that may not be used in agent URIs, and is provided for completeness:

  • fragments (parts at the end of the URI, starting with a '#').

  • Relative URIs and relative paths as part of an URI.

  • Only hostnames are allowed as authority. The userinfo part may not be used, and neither ports, IPv4-addresses and reg_name parts.

A last minor remark regarding the URI specification is the usage of escaped characters. RFC 2396 does not (explicitly) specify which encoding should be used to translate escaped octets into characters. For agent URIs this is defined to beUTF-8 encoding.

3.3.2. Example addresses

A few examples of well-formed addresses. The first is a location address, the others form addresses of agents. The last is used for an agent named Léon.

Example 3.1. Some well-formed addresses

agent://myhabitat

agent://agentserver/tryllian/system-agents/transporter

agent://agentserver/user/Application/Worker-4?all

agent://info-1.people.company/personnel/France/L%E9on

3.3.3. Habitat names

A habitat name is set by the habitat on startup. This can be set in various ways. If no name is set, the ARE must set an appropriate name.

3.3.3.1. Conventions

It is recommended that a habitat administrator uses a Fully Qualified Domain Name as the habitat's name. For running multiple habitats on one computer system, one can use CNAMEs, in coordination with the network administrator.

It is furthermore recommended that network administrators use an appropriate hierarchy in habitat naming. An hierarchy according to the well-known Domain Name System (DNS) is recommended, where the nodes come first, followed by their group, etc... Other (future) features may use such an hierarchy for optimization.

Example 3.2. Habitat naming convention

node.cluster.department.realm.world

3.3.4. Agent registration

The agent part of an address is handled by the habitat referred to in the address (the habitat in the habitat part of the address). How an agent has to register itself within this habitat is left to that habitat.

Example 3.3. Address with agent hierarchy

agent://habitat/universe/set/instance

Note

The current implementation uses JNDI for agent registration. See Section 3.5, “JNDI” and Section 3.6, “Addresses, JNDI and agent-IDs” for more information.

3.3.5. Creating an address

An address can be created by combining the scheme name with a habitat name. This will create an location address. Appending a (properly escaped) path of an agent will create an agent address. Both types are implemented in AddressURI.

An API is provided for creating addresses. It is strongly recommended developers use this API, instead of manipulating addresses directly as Strings.

3.3.6. How are addresses resolved?

An address is resolved when it is used. This happens for instance when a message is sent. Resolving a message occurs in two steps:

  1. The habitat name is taken from the address. This habitat name is resolved by the underlying communication infrastructure (e.g. JXTA). The message is sent to the remote habitat, which will resolve the remaining part of the address.

  2. The agent part of the address is resolved by the effective (remote) habitat. This part is resolved by the agent registry component (JNDI) of that habitat. This will result in the entity (agent) that is addressed.

If the habitat or the agent in the designated habitat cannot be found, the address is unresolvable.

3.3.7. Equality of addresses

An address is equal to another address, if the following rules are fulfilled:

  1. Both addresses have the same protocol, that is "agent".
  2. The habitat name of both addresses are equal, when compared as case-insensitiveStrings.
  3. The agent part of both addresses are equal, when compared as case-sensitiveStrings.

Example 3.4. Equal addresses

agent://myhabitat/some/agent is equal to agent://MYHABITAT/some/agent

Example 3.5. Unequal addresses

agent://myhabitat/some/agent is notequal to agent://myhabitat/another/agent

agent://myhabitat/some/agent is notequal to agent://myhabitat/SOME/AGENT

3.3.7.1. Resolved equality

It is possible that an agent uses multiple addresses. This is possible, since an agent may register itself under different names or at different places in the agent hierarchy (contexts in JNDI). Normally, addresses are only equal if they are syntactically equal. It is possible, that two addresses refer to the same entity. This -- called resolved equality -- is not provided by addresses, or the corresponding API. Other parts of the API or protocols will provide such a service.

3.4. IDs

Every agent and every habitat has a unique ID. These IDs are 96 bit numbers, consisting of a 32-bit grantor part and a 64-bit grantee part. The grantor contains the unique ID of the entity issuing the IDs. The grantee part denotes a unique ID within the range of the specified grantor.

Figure 3.1. Bit representation of an ID

Bit representation of an ID

The layout of the bit pattern is shown in Figure 3.1, “Bit representation of an ID” The fields shown here have the following meaning:

0

Bit reserved for future use. In current versions, this bit has the value 0.

R

A two bit number that indicates the range of the grantor. The pattern 11 is for local use grantors. The other three patterns indicate a globally available grantor.

Grantor

29 bit identifier for a grantor. Together with the ranges, there can be approximately 3 billion globally available grantors.

Tryllian has defined the number 0xFABACEAE as its default grantor number[3]

Grantee

63 bit number identifying an agent, owner or developer within a grantor-range. Possible subdivisions of these 63 bits are left to the grantor.

Externally, IDs are represented by converting both the grantor and the grantee to a hexadecimal number and separating them by a dash.

Table 3.4. Ranges for different grantors and grantees

Range nameLower boundUpper bound 
Local grantors0x60 00 00 000x7F FF FF FF 
Other grantors0x00 00 00 000x5F FF FF FF 
Grantees0x00 00 00 00 00 00 00 000x7F FF FF FF FF FF FF FF 

3.4.1. The Habitat ID

Habitat IDs are used to uniquely identify a habitat. When a habitat is started, its ID is extracted from its associated certificate. If no certificate could be found, or if no ID was specified in the certificate, a local ID is generated from the IP address of the host the habitat is running on and the value of the Identification Port property. (Note that in earlier versions of the ADK the JXTA tcp-port was used in the computation of the habitat ID. Since a single habitat can now use more than one JXTA messenger and consequently can be associated with more than one JXTA tcp-port, this has been changed.) It is also possible to set the property messenger.habitat-id to a fixed value.

Habitats with local IDs can be restricted in communication with other habitats. If more than one habitat using the same version and type of a messenger plugin that have the same habitat id reside in a local area network, the result is undefined but likely to result in disrupted communication.

For more information on extracting IDs from a certificate, refer to Section 6.1.3, “X.509 Certificates”

3.4.2. The Agent ID

AgentIDs can be used to uniquely identify an agent on a habitat. If both the grantor and the grantee part of the agent are not local, the ID is considered globally unique [4].

As opposed to Habitat IDs, Agent IDs can not be stored in a certificate.

3.5. JNDI

This section describes how the ARE provides JNDI facilities to agents running inside a Habitat. Understanding this specification requires a basic knowledge of JNDI, which can be obtained by reading Java Naming and Directory Interface™ Application Programming Interface

3.5.1. JNDI operations

The ARE supports three ways of using JNDI. They are

  • Obtain an InitialContext. This is only available to classes that run in the same classloader (and thus JVM) as the habitat, e.g. agents.

  • Use a JNDI name inside an address. AddressURI's are constructed from a habitat name and an agent name; the agent name is essentially the JNDI registration of the agent (see Section 3.6, “Addresses, JNDI and agent-IDs”).

  • Obtain the contents of a Context by querying a Habitat System Agent. This is explained in The jndi-resolve protocol.

Obtaining the InitialContext can be done in two ways. The first one is by calling the ExecutionEnvironment.getJNDIContext() method. However, the ExecutionEnvironment is only available to agents.

A more generalized method for obtaining the InitialContext is already specified in the JNDI spec. By filling a Hashtable with specific properties and passing this to the InitialContext constructor, a relevant Context can be obtained.

The properties are described in Table 3.5, “”

Table 3.5. 

NameValueRequiredExplanation
Context.INITIAL_CONTEXT_FACTORYtryllian.naming.NamingConstants.INITIAL_CONTEXT_FACTORYYesThe Factory used for creating the InitialContext.

All Contexts should only allow AddressURIs to be bound. This is done to prevent abuse of the namespace and to ensure a correctly functioning garbage collection process.

3.5.2. The ADK namespaces

The ADK provides an elaborate namespace which reflects its infrastructure and provides a great deal of flexibility. The namespace consists of atomic names, separated by the forward slash (“/”), ordered from left to right.

The top-level namespace of the ADK is aptly called adk. No other namespaces have been defined so far. However, implementations may provide extra namespaces such as ejb if needed.

Within the adk namespace, every Habitat[5] has its own namespace. The canonical name of this namespace is its HabitatID. The ARE may provide symbolic links such as the habitat name.

The Habitat namespace can be subdivided into other namespaces in a hierarchical fashion.

Hierarchically under the habitat namespace, there are three predefined namespaces. The first one is tryllian, this namespace must be managed by the ARE.

The second namespace is services, which is used with the service-address and related protocols. It contains a context for each provided service, which contains bindings for each agent that offers (registered itself as) that service.

The last predefined namespace is user. This namespace is open, meaning that everyone has read and write access to it.

The tryllian namespace is further subdivided in two namespaces: agents and system-agents. The first contains the canonical addresses of the agents resident in this habitat. The second contains the addresses of the System Agents by their name.

Example 3.6. Some example JNDI names

        adk/12345678-8765432165487657/tryllian/agents/12345678-8765432112345678
        adk/12345678-8765432165487657/services/echo
        adk/98765432-F52CED77423978AB/user/telephony/mrtelephone
        adk/769714FB-C57831BD89784321/tryllian/system-agents/transporter
      

3.5.3. Registering an address

Normal agents should register themselves within the user space. This is done using an ordinary bind operation. For every application, agents should register themselves under an application specific context, which should be named like their Java package names.

For example, Tryllian agents would register themselves under the com.tryllian.application namespace.

Service Agents are allowed to register themselves under the services namespace. However, they must follow the following conventions:

  • Service Agents must register themselves in a context named after their service. For example, an e-mail agent could register itself in the tryllian.mail context.

  • Service Agents must register themselves using a unique name. Using their Agent ID as the name is strongly recommended.

3.5.4. Symbolic names

In order to prevent unreadable JNDI names, the ARE must also understand the “Symbolic names”. There are currently only one symbolic name.

Symbolic nameDenotesAllowed
.:The current habitat Instead of the Habitat ID.

3.5.5. Resolving a JNDI name

Resolving JNDI names using the lookup method is done in several stages. First, the symbolic name .: is resolved.

When all .:s have been resolved, the name must be looked up according to the JNDI spec.

At this point it should become apparent why Service Agents must use the conventions shown in Section 3.5.3, “Registering an address”. Since all Service Agents registered under the same context should perform exactly the same task, it does not matter which Service Agent is actually addressed [6]. This makes the actual number of Service Agents completely transparent.

3.5.6. Habitat namespaces

Within the adk namespace, every known habitat is represented by its Habitat ID. So calling listBindings("adk") should give you a NamingEnumeration containing all currently known Habitat IDs (including the Habitat ID of the running platform).

Normally a habitat is known when the messenger plugin has received a message from another habitat or has received an advertisement of the other habitat through peer discovery. The ARE should make sure that a newly discovered habitat can be found in the adk namespace within a reasonable time (30 seconds). The ARE may discard Habitat IDs from the adk namespace after a certain period of inactivity (5 minutes).

In the Habitat namespace on the running habitat the tryllian and user namespaces are visible. For remote Habitat namespaces the ARE may replicate this information from the remote habitat but this is not required (and currently not implemented). If this information is required it may be available through The jndi-resolve protocol

3.5.7. Security constraints

For most contexts it is required to have the tryllian.naming.JNDIPermission in order to bind in them. The only exceptions to this are

  • The user context and its subcontexts.

  • The services context and its subcontexts.

The JNDIPermission is currently an all-or-nothing permission.

3.6. Addresses, JNDI and agent-IDs

This section explains the relation between addresses, JNDI and agent IDs. This includes the agents canonical address, resolving the JNDI part of addresses, as well as the relation with the ID of the agent.

3.6.1. Mapping addresses to JNDI

As explained in Section 3.3, “Addresses”, an address is resolved in two steps. The last part of an address is resolved by the addressed habitat. The habitat resolves the local part of an address, the agent part, using JNDI.

The agent part of an address is a 1-on-1 mapping to the context of the current (effective) habitat. In other words, the agent part of an address can be translated to a JNDI binding, by prepending adk/.:/ to it.

Note

This only applies to the effective habitat, that is the habitat that is addressed in the address.

The reverse is true as well. A full JNDI Name can be used to create an address, by using the full name minus the two first parts, which are adk and the habitat-ID.

Example 3.7. Address to JNDI mapping

agent://myhabitat/tryllian/system-agents/echo can be found in JNDI as adk/.:/tryllian/system-agents/echo on the habitat named "myhabitat". If an agent registered the JNDI binding adk/12345678-8765432112345678/user/MyAgent, that agent may be addressed as agent://yourhabitat/user/MyAgent, if that habitat is named "yourhabitat".

3.6.2. Resolving an agent's address

With the above described mapping of addresses to JNDI, addresses can be resolved to the actual addressed agent in the referenced habitat. This is done according the following mechanism:

  1. Take the agent part of the address.

  2. Prepend adk/.:/ to this path.

  3. Lookup the result in JNDI.

  4. If the agent contains an address in the context adk/.:/tryllian/agents, the name of this binding results in the ID of the addresses agent. See below.

    If JNDI contains a binding to an address for this name, the address bound is resolved as well. To avoid infinite loops, there is a maximum depth to this recursion. Currently, the maximum recursion is three levels deep.

    If JNDI contains a context for the name, one of the addresses directly (thus not in subcontexts) bound in this context is chosen at random. The resulting address is resolved again.

If no address was found with the above procedure within the given limitations, the conclusion will be that there is no such agent. This will therefore result in a NoSuchAgentException, a delivery failure message or another applicable error.

3.6.3. Agent ID

Although addresses are used throughout the ADK for communication between agents, any agent has a unique identity, its ID. This ID is the only unique, unchangeable, identification an agent has. Therefore, an address should resolve to an ID, before the ARE is able to deliver messages.

All IDs of the agents in a habitat are registered in JNDI as well. These are all registered in the context adk/.:/tryllian/agents. The name of a binding in this context is restricted to the format of an IDReference. Therefore, if an address points to a JNDI binding in the agents context, this results in the ID of the addressed agent. This allows addresses to be resolved to IDs, as described above. The object bound under the ID-name, is the canonical address of the agent, which is described below.

3.6.4. Canonical agent address

Since each agent has a unique ID, which is bound in JNDI, each agent has an unique address as well. This address is used for instance on all messages the agent sends. The default value for this address is directly derived from its ID. For example, an agent will have the default canonical address agent://habitat.name/tryllian/agents/12345678-9876543210ABCDEF. This matches the ID of the agent in the adk/.:/tryllian/agents context on the habitat with the name "habitat.name".

Although this will properly identify the agent, it is not always the most human readable form of addresses. Therefore, an agent is allowed to alter its own canonical address. An agent can set its primairily used address to a more applicable named one.

An agent can set its canonical address in two steps:

  1. Register the effective address (that is the one with that contains the ID) under the desired name.
  2. Rebind the address under the ID-based name.

The (incomplete) example below shows a way to set ones canonical address.

Example 3.8. Setting an agents canonical name

String desiredName = "user/App/foo";
ExecutionEnvironment env = ExecutionEnvironment.get();
Context context = env.getJNDIContext();
// Register the ID-based address under the new name
context.bind(NamingConstants.CURRENT_HABITAT_CONTEXT + desiredName,
             env.getCurrentAgentAddress());
// rebind the canonical address to the new address
context.rebind(NamingConstants.AGENTS_CONTEXT + myID,
               new AddressURI(desiredName));
      

There are a couple of constraints applied on the canonical address of an agent. These constraints ensure consistency and security.

  • The address to bind in adk/.:/tryllian/agents/ID should match back to this name. I.e. if adk/.:/tryllian/agents/12345678-8765432112345678 binds to agent://localhabitat/user/foobar, adk/.:/user/foobar should resolve to agent://localhabitat/tryllian/agents/12345678-8765432112345678.

  • The desired name should be bound before the canonical address (in the adk/.:/tryllian/agents context) will be bound.

  • This address must exist and must be unique and, and must remain to exist and be unique! Thus, it may not be, nor become a Context. Neither may this binding be unbound or altered as long as the canonical is in effect. The context in which this name is bound cannot be deleted as long as this address remains canonical.

  • Only the HabitatSA must be allowed to bind/unbind agents in this Context. This garantuees agents that exist do have a canonical name. An agent may only rebind its own canonical address.



[1] simple-tuple is called simple for a reason!

[2] Both on a technical level, for example by providing Trust Chains, and on an infrastructural level, i.e. no malicious hosts are trusted.

[3] As can be seen in Table 3.4, “Ranges for different grantors and grantees”, Tryllian's grantor ID is in fact a number reserved for future use.

[4] Global uniqueness of IDs may be enforced by the ARE.

[5] That is, every Habitat known by the Habitat providing the Context.

[6] If it does matter, other techniques should be used, so the specific Agent ID of that agent can be used.