ReactiveMachine is a java-written reactive framework. It implements agents, which interact in a deterministic way, by using the reactive paradigm. This framework invites to experiment with something like collaborative programming. It is not sufficiently experienced for making depend your critical system on it but your return of experience would be appreciated.
The purpose of this document is to describe origins, design and use of the ReactiveMachine software. In order to get a good understanding of it, you should be familiar with object-oriented programming and Java.
Software version | 1.0 |
Documentation version | 1.0.1 |
ReactiveMachine has mainly been inspired by reading publications around the Inria Mimosa project on reactive programming and more particularly on the Fair Threads framework. Like Fair Threads, ReactiveMachine installs an interaction system between threads, using event broadcasting. Synchronisations are realised by a community automata for the ReactiveMachine, which corresponds to a scheduler for the Fair Threads. Unlike Fair Threads, ReactiveMachine is not deterministic about thread execution order.
The community board seems to be an original proposition of the ReactiveMachine. The board idea comes from the blackboard (or whiteboard) that use scientists for sharing their theory. The board implementation design is not very surprising coming from an xmloperator project.
At the centre of the ReactiveMachine is the ReactiveCommunity. A ReactiveCommunity accepts the connection of ReactiveEntities. Together, a ReactiveCommunity and its connected ReactiveEntities assume the reactive paradigm.
Both the ReactiveCommunity and the ReactiveEntity are associated to a Java thread but the ReactiveCommunity thread is an automata that belongs to the ReactiveMachine while a ReactiveEntity thread is developped for a specific ReactiveMachine application, by using Java as programming language.
At a given time, a ReactiveEntity is either connected or not connected to a community (we use community for ReactiveCommunity). It cannot be connected to more than one community. Connecting to another community imply first disconnecting from the first one. For connecting or disconnecting its entity (we use entity for ReactiveEntity), an entity thread uses respectively the connect() or the disconnect() entity method.
Concept | Contract | Implementation |
---|---|---|
ReactiveCommunity | org.xmloperator.reactive. ReactiveCommunity | org.xmloperator.reactive.impl. ReactiveCommunityImpl |
ReactiveEntity | org.xmloperator.reactive. ReactiveEntity | org.xmloperator.reactive.impl. ReactiveEntityImpl |
A community defines instants. An instant is characterized by a long value. The activity of the connected entities occurs during instants. One instant can end only when all connected entities have agreed to that. The community makes the observation of the end of an instant and begins a new one (by incrementing its long value).
Entity connection occurs during end-of-instant. if a connection to a community is initiated at a given instant of this community by the thread of an entity connected to this community then the connection will occur at the corresponding end-of-instant elsewhere there is no guaranty on which end-of-instant the connection will occur.
An entity thread declares to stop its activity for the current instant without condition by calling the stop() method of the entity. The call return will occur at the beginning of the next instant.
An entity thread declares to stop its activity for the current instant with the condition that all the other entity threads do the same, by calling the suspend() method of the entity. If all the other entity threads do the same then the call returns null at the beginning of the next instant. But if an Event without attachment (see next section) is broadcasted during this instant then the method returns this broadcasted event at this same instant.
Events support reactive communication between the entities connected to a community. An Event belongs to its community. It has an identifier (its Object identifier) and, possibly, a name. If it has a name then this name is unique within the community.
Any connected entity (thread) can generate any event of its community. The event will be received by all the entities that are (or will be) suspended at the broadcasting instant, for an (any) event. If this event has no attachment then the broadcasting instant is the generation instant, as explained in the previous section.
An Event is or is not generated during a given instant. This is the reason why an event without attachment can be immediately broadcasted. Another entity (or the same) is allowed to generate the same event during the same instant but with no effect.
An Event with attachment cannot be broadcasted during the same instant because another entity can generate the same event with another attachment, which will be lost since event broadcasting already occurs during the instant. Broadcasting of an Event with attachment is differed to the begin of the next instant. The event is broadcasted with all the generated attachments.
The type of an Event is decided at instantiation : it will be always or never generated with attachment, but not sometimes. An Event with attachment is named a Message. A sender certified message is a message whose attachment is a CertifiedAttachment. A CertifiedAttachment contains in a certified way the identifier of the entity that has generated the message.
Concept | Contract | Implementation |
---|---|---|
Event | org.xmloperator.reactive. Event | org.xmloperator.reactive.impl. EventImpl |
Message | org.xmloperator.reactive. Message | org.xmloperator.reactive.impl. MessageImpl |
CertifiedAttachment | org.xmloperator.reactive. CertifiedAttachment | org.xmloperator.reactive.impl. CertifiedAttachmentImpl |
An entity thread must have such an organisation than it can react to the events that are emited within its community. This is the purpose of Agents. An Agent is a thread attached to a ReactiveEntity and with a standard organisation. This looks like a framework. Life time of an Agent is composed of three phases : initialisation, reaction and termination.
The reaction phase of an Agent is a loop that terminates when a given condition occurs. Within the body of this loop the Agent waits for an event or a connection. The difference between waiting and being suspended is that, when no event is generated, being suspended makes the instant change but, waiting, it doesn't. A new connection to the community makes a new instant begin and interrupts the Agent waiting.
An Agent has a persistent identity. The persistent identifier of an agent is a String composed of it full class name and, possibly, of parameter(s) value(s). Each parameter value maps either to a long value or a String one.
Concept | Contract | Abstract implementation | Framework implementation |
---|---|---|---|
Agent | org.xmloperator.reactive. Agent | org.xmloperator.reactive.agent. AgentImpl | org.xmloperator.reactive.impl. AgentThread |
An Agent may have to present several functionalities. It is useful that these functionalities be implemented separately in order to compose them freely. This is the purpose of Facets. A Facet is an agent component that assumes a part of the reaction behaviour of the agent. A Facet is attached to its Agent at agent initialisation or at a start-of-instant.
Concept | Contract | Abstract implementation | Framework implementation |
---|---|---|---|
Facet | org.xmloperator.reactive.facet. Facet | org.xmloperator.reactive.facet. FacetImpl | org.xmloperator.reactive.agent. FacetsAgent |
A Facet is focused on processing messages whose attachment is a TermsAttachment. A TermsAttachment is a CertifiedAttachment that is composed of terms. A particular term of the attachment is the service descriptor, which specify the service provided by the facet. It is facultative. Service messages are drived to the facet that delivers the specified service. This is the purpose of services : allowing some optimisation.
Concept | Contract | Implementation |
---|---|---|
TermsAttachment | org.xmloperator.reactive. TermsAttachment | org.xmloperator.reactive.impl. TermsAttachmentImpl |
An example of facet is presented here.
At each instant, the ConditionalEventFacet is responsible of generating a particular event on the condition that some events occur and/or some others doesn't. Event generation is differed (one instant later) or not differed (at the same instant).
The condition is specified by a boolean expression, which is built using the following BoolConditions:
Condition | Description | Implementation |
---|---|---|
true | The condition is true. | org.xmloperator.reactive.facet.event. TrueBoolCondition |
false | The condition is false. | org.xmloperator.reactive.facet.event. FalseBoolCondition |
event e | The condition is true if the event e occurs. | org.xmloperator.reactive.facet.event. PositiveEventBoolCondition |
not event e | The condition is true if the event e doesn't occur. | org.xmloperator.reactive.facet.event. NegativeEventBoolCondition |
c1 and c2 | The condition is true if both conditions c1 and c2 are true. | org.xmloperator.reactive.facet.event. AndBoolCondition |
c1 or c2 | The condition is true if at least one of the conditions c1 and c2 is true. | org.xmloperator.reactive.facet.event. OrBoolCondition |
It must be noted that the conclusion (i.e. the event generation) is necessarily differed when the condition contains a negation (event not occurring).
Facet | Implementation |
---|---|
ConditionalEventFacet | org.xmloperator.reactive.facet.event.ConditionalEventFacet |
The object that is responsible of launching Agents is the ReactiveMachine. The ReactiveMachine is unique within the Java virtual machine. A reference to a ReactiveMachine is obtained through a ReactiveFactory.
An agent is launched with a given profile, which it will conserve until its termination. The profile specify the operations (entity methods) that the agent can use. Any combination is possible. Standard ones are proposed, they are said restricted, standard and full. Profiles are an element for the security of the system. Launching is done by an Agent with a profile that includes the launching operation.
Another element for security is the prevention mechanism: an agent which appears to violate some given rules can be prevented to operate any more on the ReactiveMachine. Prevention is done by an Agent with a profile that includes the prevention operation. Prevention can be strong (any operation, including stop() and suspend() are prevented) or weak (only operations such as generateEvent() are prevented).
At launching, arguments are passed to the agent through a LaunchingContext. A LaunchingContext is a set of couple (name, value). Predefined argument names are defined by the Agent interface. They includes the reference to the ReactiveEntity attached to the agent and the reference to the ReactiveMachine.
There is no control on the agent persistent identifier (see the Agents section) at the ReactiveMachine level because identification is only meaningfull when participating to a particular community. Unicity of persistent identifier of the connected agents can be a rule of the community.
Concept | Contract | Implementation |
---|---|---|
ReactiveMachine | org.xmloperator.reactive. ReactiveMachine | org.xmloperator.reactive.impl. ReactiveMachineImpl |
ReactiveFactory | org.xmloperator.reactive. ReactiveFactory | org.xmloperator.reactive.impl. ReactiveFactoryImpl |
Most of the restrictions that are specified by an agent profile are implemented by the entity associated to the agent. For the others, the ReactiveMachine used a key system. A key protected method has a long key argument. Calling such a method requires to provide the good key value. Calling it with a bad key value throws a PrivilegiedAccessException. Following is the list of the ReactiveMachine methods that are protected by a key.
ReactiveMachine method | Description |
---|---|
launchAgent() | Launches an Agent. This method is dedicated to the main thread since a connected Agent can use the corresponding method of its entity. |
preventAgent() | Prevents an Agent. An Agent with a profile for preventing agents receives the corresponding key in its LaunchingContext. |
done() | Terminates the ReactiveMachine. Call returns after all agents and all communities have terminated. |
The three corresponding key values are delivered by the ReactiveFactory when creating the ReactiveMachine.
The ReactiveMachine object provides a newKey() method for generating key values.
Communities are created by agents. An agent whose profile includes the community creation operation can, usually at initialisation, create its community. Usually, such an agent is the manager, for its full life, of the community that it has created. A community is said idle when no entity is connected to it.
At creation, a community usually receives a theme. This can be a name, which identify the community. Without theme, there is no difference between a community and another one. An agent that, at initialisation, wants to connect to a community with a given theme, asks for this theme to the ReactiveMachine.
At launching, an agent may also be directly connected to a given community. This is done by adding a DESTINATION_COMMUNITY argument to the LaunchingContext. In this case, a theme is not useful.
Only the agent that created it may close a community. Another agent would throw a CommunityOwnerException.
Closure is effective once the community is idle. Once closed, a community continues to respect its contract (such as broadcasting events), excepted that it accepts no connection.
The community board answers to these questions:
A board is attached to a community and constitutes its shared memory.
A board is data. This data is invariant during an instant but can change during end-of-instants.
Concept | Contract |
---|---|
Board | org.xmloperator.reactive.Board |
For making the board (or any other environment of the community) data change during end-of-instants, Actions are used. An Action describes the operation that will be executed by the community automata during an end-of-instant. An action is transmitted using an action message. An action message is a Message whose attachment is an ActionAttachment.
An action has three possible status: proposed, executable or executed. Following is a scenario that uses these three status:
An agent generates an action message with a proposed action.
The community automata changes the action status to executable before broadcasting the action message, at the beginning of the next instant.
The community manager, an agent that is supposed to have a profile including the executable action message generation, receives the action message and regenerates it (it is supposed to accept the action), as it is.
At the end-of-instant, the action is executed by the community automata and the action status is changed to executed. If an exception is thrown by the action execution then this exception is stored in the ActionAttachment.
The action message is broadcasted, at the beginning of the next instant.
This scenario allows a full control of actions. An agent with a good profile can also directly generate an executable action message.
Reversing an action is not proposed because usually not deterministic.
Concept | Contract | Implementation |
---|---|---|
Action | org.xmloperator.reactive. Action | org.xmloperator.reactive.impl. ActionImpl |
ActionAttachment | org.xmloperator.reactive. ActionAttachment | org.xmloperator.reactive.impl. ActionAttachmentImpl |
The ReactiveMachine proposes a concrete implementation of board. This is a hierarchical structure of elements that looks very like the Document Object Model (DOM) of the W3C. An advantage of such a structure is that it can be easily read or written as an XML content.
An Element is either a ParentElement, a DataElement or an empty element. A ParentElement can have children elements, a DataElement cannot, neither an empty one. Any Element has a local name and, possibly, a namespace URI.
A DataElement is a recipient for a data with a given data type, which corresponds to a Java type. Used data types are: long, double, string and object. An object data type corresponds to a TranslatableObject. A TranslatableObject is an Object with a method for translating it to an Element and another method that is used for instanciating an Object, called Rebuilder, which is able to rebuild the TranslatableObject from its representing Element.
Conformant to the board contract, all element attributes are readable (by any agent) but none are (directly) writable. TranslatableObjects have to respect the same contract.
Concept | Contract | Implementation |
---|---|---|
Element | org.xmloperator.reactive.element. Element | org.xmloperator.reactive.element.impl. ElementImpl |
ParentElement | org.xmloperator.reactive.element. ParentElement | org.xmloperator.reactive.element.impl. ParentElementImpl |
DataElement | org.xmloperator.reactive.element. DataElement | org.xmloperator.reactive.element.impl. [Long|Double|String|Object]DataElementImpl |
TranslatableObject | org.xmloperator.reactive.element. TranslatableObject | |
Rebuilder | org.xmloperator.reactive.element. Rebuilder |
The board implementation provides through the ElementWritingAccess interface a method that returns for any Element a corresponding RWElement with writing methods. This method requires a key value (see the Key system section) that doesn't depend on the Element. This key value is delivered to the ReactiveCommunity when setting its board. In that way, the community and only it is allowed to touch the board, during end-of-instants.
Concept | Implementation |
---|---|
Board | org.xmloperator.reactive.element.impl. ElementBoardImpl |
The board implementation is also a RootElement implementation. A RootElement is a ParentElement. Namespaces are managed at its level.
Concept | Contract | Implementation |
---|---|---|
RootElement | org.xmloperator.reactive.element. RootElement | org.xmloperator.reactive.element.impl. ElementBoardImpl |
Saving and restoring a RootElement are respectively assumed by the DocumentWriter and DocumentReader objects.
Object | Implementation |
---|---|
DocumentWriter | org.xmloperator.reactive.element.util. DocumentWriter |
DocumentReader | org.xmloperator.reactive.element.util. DocumentReader |
The following Actions on Element are available:
Action | Description | Implementation |
---|---|---|
Insert | A given element is inserted before a reference Element. | org.xmloperator.reactive.element.action. ElementInsertAction |
Append | A given Element is appended as last child of a reference ParentElement. | org.xmloperator.reactive.element.action. ElementAppendAction |
Remove | A reference Element is removed. | org.xmloperator.reactive.element.action. ElementRemoveAction |
ChildrenRemove | The children of a reference ParentElement are removed. | org.xmloperator.reactive.element.action. ChildrenRemoveAction |
ChildrenSort | The children of a reference ParentElement are sorted using a Comparator of Element. | org.xmloperator.reactive.element.action. ChildrenSortAction |
Constraints can be set on the children elements of a ParentElement. The following ChildrenConstraints are available:
Constraint | Description | Implementation |
---|---|---|
DifferentNames | Two children elements of a ParentElement cannot have the same name (local name and namespace URI). | org.xmloperator.reactive.element.constraint. DifferentNamesChildrenConstraint |
FixedName | All the children elements of a ParentElement have the same given name (local name and namespace URI). | org.xmloperator.reactive.element.constraint. FixedNameChildrenConstraint |
ChildCount | The count of the children elements of a ParentElement cannot be less than a min value after a remove operation neither greater than a max value (-1 for no max value) after an insert/append operation. | org.xmloperator.reactive.element.constraint. ChildCountChildrenConstraint |
An ElementAction whose execution is contrary to a ChildrenConstraint returns a ChildrenConstraintException in the ActionAttachment.
Concept | Contract |
---|---|
ChildrenConstraint | org.xmloperator.reactive.element.ChildrenConstraint |
More flexible than ChildrenConstraint is Action rejection. At the contrary of ChildrenConstraint, Action rejection operates before Action execution, when the Action has yet the status proposed (see explanations on Action status in the Actions section). An ActionRejector decides if a given Action has to be rejected.
Concept | Contract |
---|---|
ActionRejector | org.xmloperator.reactive.element.action.reject. ActionRejector |
Following are the available action rejectors on elements:
ActionRejector | Description | Implementation |
---|---|---|
Element modify rejector | Adding or removing children to any Element that conforms to a given ElementSpecification has to be rejected. | org.xmloperator.reactive.element.action.reject. ElementTouchRejector .newModifyRejector(ElementSpecification) |
Element remove rejector | Removing any Element that conforms to a given ElementSpecification has to be rejected. | org.xmloperator.reactive.element.action.reject. ElementTouchRejector .newRemoveRejector(ElementSpecification) |
An ElementSpecification decides if an Element is conformant or not to it.
Concept | Contract |
---|---|
ElementSpecification | org.xmloperator.reactive.element.spec. ElementSpecification |
Following are the available ElementSpecifications:
ElementSpecification | Description | Implementation |
---|---|---|
Root | The board root Element. | org.xmloperator.reactive.element.spec. RootElementSpecification |
ElementName( localName , namespaceURI ) | Any Element that has the given local name and namespace URI (which can be null). | org.xmloperator.reactive.element.spec. ElementNameSpecification |
Child( localName , namespaceURI , parentSpecification ) | Any Element that has the given local name (if not null) and namespace URI (which can be null) and is child of a parent that conforms to the given specification. | org.xmloperator.reactive.element.spec. ChildElementSpecification |
IncludingDescendance( elementSpecification ) | Any Element that conforms to the given specification plus any descendant Element. | org.xmloperator.reactive.element.spec. IncludingDescendanceElementSpecification |
And( elementSpecification1 , elementSpecification2 ) | Any Element that conforms to both the two given specification. | org.xmloperator.reactive.element.spec. AndElementSpecification |
AndNot( elementSpecification1 , elementSpecification2 ) | Any Element that conforms to the first given specification but does'nt conform to the second one. | org.xmloperator.reactive.element.spec. AndElementSpecification |
ActionRejectors and ElementSpecifications are TranslatableObjects (see the element based board section). An ActionRejector is stored in the community board as an ObjectDataElement, child of a ParentElement named "rejects". There can be several "rejects" ParentElement in the board. Also, in order to know what "rejects" ParentElements may contain the ActionRejectors that can be concerned by a given action, the following conventions are applied:
Action rejection is processed by a facet named ActionExecutionFacet. It is usually attached to the agent that manages the community. An ActionExecutionFacet regenerates the executable actions that no ActionRejector rejects.
Facet | Implementation |
---|---|
ActionExecutionFacet | org.xmloperator.reactive.facet. ActionExecutionFacet |
An Agent whose a proposed action is rejected is not considered as violating a rule. It will not be prevented for that.
The following packages of package can be constitued (each package depends on the previous ones):
The ReactiveMachine software is provided in source form exclusively. Its execution display needs a Java development platform, such as Eclipse.
In order to install the software with Eclipse you have to:
The software uses any JRE System Library that includes an XML parser.
Running the software (Run / Run...) needs to specify its main class: org.xmloperator.reactive.test.Test
A trace system is avalaible for debugging. It is activated by calling the setTrace() method of ReactiveFactoryImpl (see the ReactiveMachine section). Trace activation operates at the class level: if traces are on for a class then all the instances of this class will generate traces.
The classes that propose traces are the following:
Following are examples of traces:
Trace | Comments |
---|---|
A1 is [..]CommunityManagerAgent | The agent number 1 is launched. Its persistent identifier is the full class name with no parameter. |
A2 is [..]SyracuseAgent/13 | The agent number 2 is launched. Its persistent identifier is the full class name with an int parameter value. |
A1 starts | Agent 1 starts. |
C1 starts | The community number 1 starts. |
C1 instant 1 | Community 1 : instant 1 begins. |
C1 A1 close | Agent 1 closes the community 1. |
C1 idle | Community 1 has no connected entity. |
C1 terminates | Community 1 is done because closed and idle. |
A1 terminates | Agent 1 is done. |
ReactiveMachine is done | All communities are closed and all agents terminated. |
C1 A1 E1 is "clock" | Within community 1, agent 1 creates the event number 1, named "clock". |
C1 A1 generate E1 | Within community 1, agent 1 generates the event 1. |
C1 broadcast E1 | Event 1 is broadcasted within community 1. |
A2 has processed E1 | Agent 2 has received and processed event 1. |
C1 A1 MC1 is "salutation" | Within community 1, agent 1 creates the certified message number 1, named "salutation". |
C1 A1 generate MC1 A1("Hello") | Within community 1, agent 1 generates the certified message 1 with an attachment composed of the term "Hello". |
C1 broadcast MC1 | Certified message 1 is broadcasted with all its attachments within community 1. |
C1 A2 generate MCA1 A2(P[.3]append<turtles>()) | Agent 2 proposes, through the action message 1, to appending a child element named "turtles" to the third child element of the board root element. |
C1 A1 generate MCA1 A1(E[.3]append<turtles>()) | Agent 1 verifies the proposed action message 1 and regenerates it for execution. |
C1 A1 action X[.3]append<turtles>() fails by [..]ChildrenConstraintException: D | Appending an element named "turtles" to the third child element of the board root element has failed because there is already a child element named "turtles". A ChildrenConstraint attached to the root third child element (whose name is "projects") specifies that its children must have different names. |
A2 proposal E[]append<wiki>() is rejected | Proposal of appending a "wiki" element to the board root element has been rejected. The reason, not given in the trace, is that any adding or removing child to/from the board root element is rejected. This is specified by an ActionRejector located in the "rejects" child element of the board root element. |
The test program Test.main() is a sequence of test procedures. Each test procedure executes the following instructions:
A ReactiveMachine is instancied using the ReactiveFactoryImpl.REACTIVE_FACTORY.
A first Agent is launched. It creates a ReactiveCommunity. The reference to this community is usually returned to the main thread through the DESTINATION_COMMUNITY argument of the LaunchingContext.
Other agents may be launched and connected to this community. They interacts with the first one during some instants and terminate.
The first agent close the community while terminating.
The ReactiveMachine.done() method waits for any agent is terminated and any community closed before returning. The test procedure is terminated. A new one can begin.
A CrashingReport object stores data about Agent crashing. If no crash is detected then the following message is printed on System.out:
No agent crashes
Concept | Contract | Implementation |
---|---|---|
CrashingReport | org.xmloperator.reactive. CrashingReport | org.xmloperator.reactive.test. CrashingReportImpl |
The subjects and sections concerned by the different test procedures are the following:
Test procedure | Subject or class | Methods | Section |
---|---|---|---|
test_3_stops_and_2_suspends | org.xmloperator.reactive. ReactiveEntity | stop() suspend() | Instants |
org.xmloperator.reactive. Agent | continues() | Agents | |
org.xmloperator.reactive.agent. AgentImpl | newCommunity() | Create a community | |
test_globalExit | org.xmloperator.reactive.agent. GlobalExitAgent | Events | |
Preventing an Agent that violates a rule or tries to do so | Security | ||
test_clock | org.xmloperator.reactive.agent. ClockAgent | Events | |
org.xmloperator.reactive. Agent | processEvent() | Agents | |
test_conditionalEvent | org.xmloperator.reactive. ReactiveEntity | createEvent() searchEvent() generateEvent() | Events |
org.xmloperator.reactive. Agent | init() continues() terminate() | Agents | |
org.xmloperator.reactive.facet. ExitFacet | Facets | ||
org.xmloperator.reactive.agent. FacetsAgent | addFacet() | Facets | |
org.xmloperator.reactive.facet.event. ConditionalEventFacet | Conditional Events | ||
org.xmloperator.reactive.agent. AgentImpl | newCommunity() | Create a community | |
test_Syracuse: org.xmloperator.reactive.test. SyracuseAgent | org.xmloperator.reactive.impl. AgentImpl | addParameter() | Agents |
org.xmloperator.reactive. ReactiveEntity | launchAgent() | ReactiveMachine | |
test_board | org.xmloperator.reactive. Agent | generateActionMessage() | Actions |
org.xmloperator.reactive.element.impl. ParentElementImpl | Element based board | ||
Reading the board at initialisation and writing it at closure. | Saving and restoring a board | ||
org.xmloperator.reactive.element.action. ElementAppendAction | Actions on elements | ||
org.xmloperator.reactive.test. CommunityManagerAgent | Action_rejection |
Last update: 2008-08-30 | Copyright (c) 2008 The_xmloperator_project |