Fork me on GitHub

Naming and Namespaces in SARL

In a SARL system, multiple elements are created at run-time, e.g. agents, behaviors, skills, spaces, contexts, etc. Each of these elements is identified by an unique universal identifier (UUID). This ensures that the identifiers are unique all the time.

Having access to the SARL elements above in a almost generic way is sometimes needed. For example, if you would like to observe the values of the fields of an agent, you need first to refer to the agent itself.

In order to help the SARL developers to do this referring, a generic and common API is defined for naming the SARL elements. This naming API is used by the namespace service in order to retrieve and reply the instances of the referred elements.

1. Naming of the SARL components

Basically the naming of the SARL element following the international standards of the Universal Resource Identifiers, or URI in short.

1.1. Background on URIs

An URI is a string of characters that unambiguously identifies a particular resource. To guarantee uniformity, all URIs follow a predefined set of syntax rules. Such identification enables interaction with representations of the resource over a system or a network (typically the World Wide Web), using specific schemes.

Each URI following the general following format (where parts between brackets are optional):

scheme:[//authority]path[?query][#fragment]

The URI generic syntax consists of a hierarchical sequence of five components:

This general URI syntax is refined in order to refer the SARL components.

1.2. Naming for Agents

Each agent may be referred by an URI-based name in which the scheme is always agent. There is neither authority nor query part in the agent name.

The path of the agent name specifies the identification of the agent. You could refer an agent in three ways:

Where, b9e6dcbc-d878-441d-afa1-35715950e22d is the context identifier, 0bec6efd-12b1-4394-8e34-1b56e6b99c5c is the space identifier, and a7fbd4cc-9e1a-48c3-8ee8-3a7974ccb05c is the agent identifier.

The fragment part is the name of an attribute/field that is declared into the referred agent. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the agent names is defined by the following BNF grammar:

AGENT_NAME = "agent:" <ODSL> <CTX> <UUID> <FRG>
ODSL = "/" OSL | <empty>
OSL = "/" | <empty>
CTX = <UUID> "/" SPC | <empty>
SPC = <UUID> "/" | <empty>
FRG = "#" <ID> | <empty>

1.3. Naming for Behaviors

Each behavior of an agent may be referred by an URI-based name in which the scheme is always behavior. There is neither authority nor query part in the behavior name.

The path of the behavior name specifies the identification of the behavior. A behavior is always attached to an agent. That’s why, the agent identifier is mandatory in all cases. Consequently, for each of the three cases for referring an agent, two cases are defined for referring a behavior of those agent:

Where, a7fbd4cc-9e1a-48c3-8ee8-3a7974ccb05c is the agent identifier. mypackage.MyBehavior is the fully qualified name of the behavior type to refer. It must be a sub-type of the io.sarl.lang.core.Behavior type that is defined into the SARL API.

The fragment part is the name of an attribute/field that is declared into the referred behavior. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the behavior names is defined by the following BNF grammar (BNF rules in the previous section are re-used):

BEHAVIOR_NAME = "behavior:" <ODSL> <CTX> <UUID> "/" <ID> <IDX> <FRG>
IDX = "/" <INTEGER> | <empty>

1.4. Naming for Skills

Each skill of an agent may be referred by an URI-based name in which the scheme is always skill. There is neither authority nor query part in the skill name.

The path of the skill name specifies the identification of the skill. A skill is always attached to an agent. That’s why, the agent identifier is mandatory in all cases. Moreover, a capacity must be implemented by only one skill within an agent. But a skill may implement multiple caapcities. Consequently, for each of the three cases for referring an agent, one case is defined for referring a skill of those agent:

Where, a7fbd4cc-9e1a-48c3-8ee8-3a7974ccb05c is the agent identifier. mypackage.MyCapacity is the fully qualified name of the capacity type that is implemented by the skill to refer. It must be a sub-type of the io.sarl.lang.core.Capacity type that is defined into the SARL API.

The fragment part is the name of an attribute/field that is declared into the referred skill. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the skill names is defined by the following BNF grammar (BNF rules in the previous sections are re-used):

SKILL_NAME = "skill:" <ODSL> <CTX> <SPC> <UUID> "/" <ID> <FRG>

1.5. Naming for Context

Each agent context may be referred by an URI-based name in which the scheme is always context. There is neither authority nor query part in the context name.

The path of the context name specifies the identification of the context. You could refer a context as:

Where, b9e6dcbc-d878-441d-afa1-35715950e22d is the context identifier.

The fragment part is the name of an attribute/field that is declared into the referred context. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the agent names is defined by the following BNF grammar (BNF rules in the previous section are re-used):

CONTEXT_NAME = "context:" <ODSL> <UUID> <FRG>

1.6. Naming for Space

Each space within a context may be referred by an URI-based name in which the scheme is always space. There is neither authority nor query part in the space name.

The path of the space name specifies the identification of the space. A space is always defined into a context. That’s why, the context identifier is mandatory in all cases. Consequently, for each of the cases for referring a context, you could refer a space as:

Where, b9e6dcbc-d878-441d-afa1-35715950e22d is the context identifier, and 0bec6efd-12b1-4394-8e34-1b56e6b99c5c is the space identifier.

The fragment part is the name of an attribute/field that is declared into the referred space. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the space names is defined by the following BNF grammar (BNF rules in the previous section are re-used):

SPACE_NAME = "space:" <ODSL> <UUID> "/" <UUID> <FRG>

1.7. Naming for Service

A SRE may implement services. Each service may be referred by an URI-based name in which the scheme is always service. There is neither authority nor query part in the service name.

The path of the service name specifies the identification of the service, i.e. its fully qualified name. You could refer a service with:

Where, mypackage.MyService is the fully qualified name of the object interface that describes the service.

The fragment part is the name of an attribute/field that is declared into the referred service. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the service names is defined by the following BNF grammar (BNF rules in the previous section are re-used):

SERVICE_NAME = "service:" <ODSL> <ID> <FRG>

1.8. Naming for Artifact

According to the Agent&Artifact metamodel, an artifact in a multi-agent system is a component of the agent environment Each artifact may be referred by an URI-based name in which the scheme is always artifact. There is neither authority nor query part in the service name. Please note that because the concept of artifact is not related to the agent society directly, the underlying SRE may not provide a support for it.

The path of the artifact name specifies the identification of the artifact, i.e. its identifier. You could refer a artifact with:

Where, 37b13185-a9d5-43e5-9d7b-da2fa3ba3d54 is the identifier of the artifact.

The fragment part is the name of an attribute/field that is declared into the referred artifact. If the fragment part is specified, then the URI refers to the field itself.

The general syntax of the service names is defined by the following BNF grammar (BNF rules in the previous section are re-used):

ARTIFACT_NAME = "artifact:" <ODSL> <UUID> <FRG>

2. Namespace Service

2.1. General Principles

According to the public API of the SRE, it is possible to retrieve a service that is implemented and executed by the SRE. A service dedicated to finding SARL elements into the SRE environment based on their names is defined into the SARL API. It is named the Namespace service.

The role of the Namespace service is to search for a SARL element based on a given name (as defined above). This service explores the entire content of the SRE in order to find the requested element. If an object that is corresponding to the given name is found, then the Namespace service replies the found object, or an accessor to its field if a fragment part was specified into the given name.

The Namespace service is defined as:

interface NamespaceService {
	def findObject(SarlName) : Object
	def findObject(SarlName, Class<T>) : T with T
	def findObject(String) : Object
	def findObject(String, Class<T>) : T with T
	def findObject(URI) : Object
	def findObject(URI, Class<T>) : T with T
	def getFieldAccessValidator : IFieldAccessValidator
	def getNameParser : INameParser
}

The functions findObject search for an object based on the given name (whatever it is an object of type SarlName representing the super-type of all the names, an URI, or a string representation of an URI).

To use this service, you have to get it from the SRE, as illustrated below:

var bootstrap = SRE::getBootstrap
var namingService = bootstrap.getService(typeof(NamespaceService))
var theAgent = namingService.findObject("agent:a7fbd4cc-9e1a-48c3-8ee8-3a7974ccb05c")

2.2. Field Accessor

In the case the given name targets a field (when it has a fragment part), the Namespace service create a specific proxy of type FieldAccess in order to have access to the field. This type is defined as:

class FieldAccess {
	def get : Object
	def getField : Field
	def getInstance : Object
	def getName : SarlName
	def isWritable : boolean
	def set(Object) : Object
	def toString : String
}

This accessor type enables to have access to the object instance and to the field value. However, the instance of FieldAccess is provided only if the field is observable.

3. Observable and Not Observable Fields

Observation means that data or information could be extracted from the observed object. Because an agent is defined as an autonomous entity, the agent should be able to enable or disable the access to one of its fields.

In the standard SRE, this observability flag could be defined statically by annotating the obsevable field, or one of its enclosing type, with the @Observable annotation. A second standard method is to implement a dedicated agent skill implementing the capacity FieldAccessValidationCapacity that enables the agent to manage the access rights to its fields dynamically.

By default, into the SRE, the algorithm for checking the field access from an invoking entity is:

3.1. @Observable Annotation

In the following example, two fields are defined for the agent MyAgent. The first field is named observableField and it is marked as observable because it is annotated with @Observable. The second field is named notObservableField and it is not observable.

agent MyAgent {
	@Observable
	var observableField : int
	var notObservableField : int
}

Consequently, even if the field notObservableField is declared, it will never be found by the Namespace space because it is hidden.

As described above, the @Observable annotation could be attached to one of the enclosing type in order to mark all the declared fields within a type as observable. In the following example, the two fields observableField1 and observableField2 are observable because the type MyAgent2 is marked with the @Observable annotation.

@Observable
agent MyAgent2 {
	var observableField1 : int
	var observableField2 : int
}

3.2. Capacity to Manage the Access Rights

In order to enable an agent to manage the accesses to its own fields, you could equip this agent with a skill that implements the FieldAccessValidationCapacity capacity:

capacity FieldAccessValidationCapacity {
	def getFieldAccessRight(Field) : FieldAccessRight
}

When an agent owns a skill implementing FieldAccessValidationCapacity, this skill is included into the right checking for accessing to the fields of the agent, its behaviors and its skills.

Let the agent implementation below:

agent MyAgent {
	var field1 : int
	var field2 : int
	on Initialize {
		setSkill(new AccessRightSkill)
	}
}

In this agent, two fields are defined and named field1 and field2. We equip the agent with the AccessRightSkill skill that implements the FieldAccessValidationCapacity capacity:

skill AccessRightSkill implements FieldAccessValidationCapacity {
	def getFieldAccessRight(field : Field) : FieldAccessRight {
		if (field.name == "field1") {
			return FieldAccessRight::READ
		}
		if (field.name == "field2") {
			return FieldAccessRight::WRITE
		}
		return FieldAccessRight::NONE
	}
}

This skill gives the reading access to the field field1, and the reading/writing accesses to the field field2. Any other field cannot be observed.

Three levels of obersavility are defined into the enumeration FieldAccessRight:

Copyright © 2014-2023 SARL.io, the Original Authors and Main Authors.

Documentation text and medias are licensed under the Creative Common CC-BY-SA-4.0; you may not use this file except in compliance with CC-BY-SA-4.0. You may obtain a copy of CC-BY-4.0.

Examples of SARL code are licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the Apache License. You may obtain a copy of the Apache License.

You are free to reproduce the content of this page on copyleft websites such as Wikipedia.

Generated with the translator docs.generator 0.14.0-SNAPSHOT.