Agents have a lifecycle that consists out of multiple parts. For the first part, OSGi Declarative Services is used to create and activate the component. This can either be a singleton object, but usually the factory pattern is used, such that for each configuration that is created, a new component is created (see TODO for a tutorial on this).
The component definition for an agent should look something like this:
@Component(designateFactory = ExampleAgent.Config.class,
provide = { ObservableAgent.class, AgentEndpoint.class })
public class ExampleAgent extends BaseAgentEndpoint {
public static interface Config {
@Meta.AD(deflt = "concentrator")
String desiredParentId();
@Meta.AD(deflt = "freezer")
String agentId();
// Other configuration parameters
}
@Activate
public void activate(Map<String, Object> properties) {
config = Configurable.createConfigurable(Config.class, properties);
activate(config.agentId(), config.desiredParentId());
// Handle other configuration parameters
}
@Override
public void setContext(FlexiblePowerContext context) {
super.setContext(context);
// Here you can use the context to schedule a recurring job, for example:
scheduledFuture = context.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
doBidUpdate();
}
}, Measure.valueOf(0, SI.SECOND), Measure.valueOf(config.bidUpdateRate(), SI.SECOND));
}
// Here you can place your own logic for creating bids and handling price updates
void doBidUpdate() {
// TODO create new bid and call publishBid()
}
@Override
public void handlePriceUpdate(PriceUpdate priceUpdate) {
// Do something with the new price
}
}
In this example, multiple instances of this agent can be created, because the designateFactory
option of the component annotation is used. So for each configuration (as defined by the Config
interface) a new instance of this agent is created. Also each agent is registered in the service registry as an AgentEndpoint
and an ObservableAgent
(see the provide
option). This means that after activation this agent can be found by the PowerMatcher runtime.
The PowerMatcher runtime will do 2 things after it detects a new AgentEndpoint
or MatcherEndpoint
:
- It will always call the
setContext
method to give the agent itsFlexiblePowerContext
instance. This object can be used to get the system time (which could be simulated!) and schedule jobs in the future (similar to having aScheduledExecutorService
). - If the agent implements the
AgentEndpoint
and it has found theMatcherEndpoint
that has a matchingdesiredParentId
, it will start up aSession
between the two. For theAgentEndpoint
this will result in a call to theconnectToMatcher
method (already implemented in theBaseAgentEndpoint
) to give the reference to the session. This will in turnconfigure
the agent with itsMarketBasis
andclusterId
.
Only after the context has been set and the agent has been configured (either through connecting a session for a device agent or a concentrator, or by activation for the auctioneer), the agent is connected and can be used normally. This can be checked through the Agent.isConnected()
method.
Here is a sequence diagram that describes this lifecycle: