Components

With Jadex, the behaviour of a software is defined by the interaction between components, each of them providing a clearly defined functionality.

When you create a new component, you have to choose between different component types. For now, we will focus on Micro Agents, the most basic type of component. For other component types, please refer to Component Types.

For a more complete guide into Active Components, take a look at the AC User Guide.

Implementation

Micro Agents are defined by plain java classes. In order for a java class to represent a Micro Agent, two requirements have to be met:

  • The name of the class has to end with "Agent" (e.g. MyAgent, ChatAgent, ...)
  • The class has to be annotated with the @Agent Annotation

Optionally, it can provide a description using the @Description Annotation. The value is then shown inside the JCC.

This leads to the following code for a basic micro agent:

package tutorial;

@Agent
public class MyAgent
{
}

In order for you Agent to do something once it is started, you can provide an Agent Body method. This is an arbitrary named public method without parameters annotated with @AgentBody:

@AgentBody
public IFuture<Void> body()
{
    System.out.println("Hello World!");
}

While implementing your component, keep in mind that components in Jadex are always Single-Threaded! Concurrency only takes place between different components, which will spare you a lot of headaches. Please don't start your own threads, as this will lead to errors later on.

Instead of the return type IFuture<Void>, you can also use void. Using a futurized return type allows you to perform work asynchronously, which is handled in chapter Futures.

Startup

Starting of components is done by the Platform's ComponentManagementService (CMS). Service instances in general can be retrieved using the static methods of the SServiceProvider class.

Obtaining the CMS

Remember the IExternalAccess platform object that you got when starting a platform? It is now required to retrieve the CMS:

IExternalAccess platform = Starter...
IFuture<IComponentManagementService> fut = SServiceProvider.getService(platform, IComponentManagementService.class);

IComponentManagementService cms = fut.get();

Notice how we use the interface of the service we want to retrieve? In Jadex, Java interfaces are used for the interaction with services, so the implementation can remain hidden.

Starting the component

Once you get a reference to the CMS, you can use the createComponent() methods to start your components (See API documentation of IComponentManagementService).

The preferred method to start a component has the following signature:

ITuple2Future<...> createComponent(String name, String model, CreationInfo info);

You may provide a CreationInfo object (e.g., to pass parameters at startup) or a name for the created component instance, but most importantly, you have to provide a model, which in this case is simply the fully qualified name of the component class (or XML-file for other component types):

ITuple2Future<...> fut = cms.createComponent("myAgent1", "tutorial.MyAgent.class", null);
IComponentIdentifier cid = fut.getFirstResult();
System.out.println("Started component: " + cid);

Notice how you get a different Future object this time? A Tuple2Future represents a promise that two different results are going to be available. In this case, the first is the ComponentIdentifier, which is used to identify the instantiated component. The second result is a Map that can be filled with results by the component and is only returned upon termination of the component. You can use fut.getSecondResult() to block until the component is terminated and receive the results. Take a look at Futures for different future types.

Now that you know how to start your own components, you can read more about Services, as they provide a way for components to interact with each other.

Destroying the component

To destroy a component, the CMS has to be used again. Call destroyComponent(cid) and pass the Component Identifier returned on component startup:

Map<String,Object> results = cms.destroyComponent(cid).get();

If the component has any results, they are contained in the returned map. This is the same Map that is provided by the ITuple2Future received upon starting the component.

Component Arguments

Declaring Arguments

Components can declare arguments that can be passed during creation. To declare arguments, use the @Arguments Annotation:

@Arguments(@Argument(name="myName", description = "Name of this agent", clazz=String.class, defaultvalue = "\"Hugo\""))
public class MyAgent ...

Because the defaultvalue is parsed, Strings have to be quoted. You can also use other (Java) expressions that are executed to determine the default value. To access this argument from inside the agent, inject it into a field using the @AgentArgument annotation:

@AgentArgument
protected String myName;

Note that the field and argument name must match. If you want your field to have another name, specify the argument name as parameter when using the annotation: @AgentArgument("myName"). Another way to access the arguments of an agent is by using the IArgumentsResultsFeature.

Passing Arguments

When you created a component as explained above, the last parameter ofcreateComponent was null. Instead, you can create your own CreationInfo object containing your component's arguments and pass it in createComponent:

CreationInfo ci = new CreationInfo(SUtil.createHashMap(new String[]{"myName"}, new Object[]{"Harald"}))

Component Features

All component functionalities are available via features. By default, all components have a certain set of features, which can be injected into fields by using an annotation:

@AgentFeature
IExecutionFeature exeFeat;

Below is a list of important features commonly available for all components. For features specific to a component-type, take a look at component types.

Feature Name Description
IArgumentsResultsFeature Provides access to agent arguments and can take agent results.
IExecutionFeature Provides access to the execution model to schedule component steps and provide wait functions.
IMessageFeature Handles sending and reception of messages between components.
IMonitoringComponentFeature Components can publish monitoring events with this feature.
IRequiredServicesFeature See Services
IProvidedServicesFeature See Services

Component Lifecycle

The Jadex Active Components Platform and the CMS implement a specific lifecycle for components. For each step in the cycle there is an annotation which can be used on methods to perform actions during the lifecycle step. These annotations can be used on methods, like this:

@AgentCreated
public IFuture<Void> agentCreated() {...

All annotations also allow for methods with parameters, see Parameter Guesser.

Annotation Description
@AgentCreated A method marked with this annotation will be called upon creation of the agent. This means services, injected fields etc. are not initialized at this point.
@AgentBody A method marked with this annotation will be called after creation of the agent is complete. At this point, all fields and required services are available and can be used.
@AgentKilled A method marked with this annotation will be called just before the component is removed from the platform.

Advanced Topics

This section discusses some of the more advanced topics regarding components.

Composition

Components can be in a hierarchy to express compositional relationship. To declare subcomponents, you may use the @ComponentTypes annotation and then create a @Configuration that includes an instance of the desired subcomponent like this:

@Configurations(@Configuration(name = "default", components =  {@Component(type = "MyChildAgent")}))
@ComponentTypes(@ComponentType(name="MyChildAgent", clazz=ChildAgent.class))
public class ParentAgent { …

Any services provided by subcomponents using the scope RequiredServiceInfo.SCOPE_COMPONENT can then be accessed using the same scope in the parent component or any other subcomponents. Please refer to the AC Tutorial for a more complete example.

More Annotations

The most important annotations common to all components were already discussed. For a full reference, have a look at the jadex.micro.annotation package.

Parameter Guesser

Each Jadex Active Component has a Parameter Guesser that is used for annotation-based injections, e.g. when using @ServiceComponent inside Services or @AgentFeature inside Components.
When using one of these annotations on methods or fields, fields and method parameters declared with the following types are filled with values, if possible:

  • IInternalAccess
  • IExternalAccess
  • Subtypes of IComponentFeature (see Component Features)
  • Subtypes of ICapability (for bdiv3 components)
  • Type of your Component - to inject the Component POJO

Scheduling steps

The concurrency model of Jadex Active Components is based on single-threaded components. If you want to execute your code on a component's thread from outside, you can call scheduleStep on the IExternalAccess of a component:

extAcc.scheduleStep(iAccess -> {
    // now you are on the component's thread
    return Future.DONE;
});

If you schedule a step on a remote component, Jadex will send the step instance to the remote platform, where they are re-instantiated. While normal inner classes have access to their surrounding instance, component steps do not! Be sure to only call static methods from inside a component step.