Some interaction constraints are functional: they expect that the method in the component, given a set of input arguments, will always unify the non instantiated arguments with the same values, or will succeed or fail, independently of whether the peer that has downloaded and executed the component. For example, the constraint sort(List,SortedList) for sorting a list of elements should always unify SortedList with the ordered version of List, even though different peers may have OKCs that implement different algorithms for sorting it.
Other components work as a bridge between the interaction model and the peer local knowledge, and will unify non instantiated variables with values that depend on the peer in which the OKC is running. For example, a constraint price(Product, Price) expects that the corresponding method in the OKC unifies the variable Price with the price assigned to Product by the peer, possibly accessing the database local to the peer: different peers may have different prices for the same product. Moreover, the same peer can be involved in many interactions simultaneously, and the peer local knowledge (or state) is changed by one interaction and read in another. For example, a peer selling products will have the total amount of available products reduced after each successful selling interaction.
The scope of an OKC instance is a single interaction: it cannot share data with runs of other interactions. However, an OKC often needs to access persistent information. For example, an interaction for the purchase of some product may need to access the peer catalogue, and may change the state of the peer’s warehouse. An issue to consider in defining the access to the peer from the OKC is that OKCs and peers may be developed by different entities, and there cannot be many assumptions. The mechanism for accessing the peer state, therefore, relies on adaptors.
An OKC declares the methods it needs to access in the peer using the Java5 annotation @RequiredPeerAccess, listing the method signatures in the peer:
@RequiredPeerAccess(
methods={"doSomething(name)","doSomethingElse(text)"}
)
The peer will have a class implementing the dummy interface PeerAccess, where exposes the methods it provides to the OKCs. The methods must be annotated with @MethodSemantic, and the arguments are all of type Argument.
class MyPeerAccess implements PeerAccess{
@MethodSemantic(
language="tag",
args={"name"}
)
public boolean doSomething(Argument n){...}
@MethodSemantic(
language="tag",
args={"text"}
)
public boolean doSomethingElse(Argument t){...}
The peer, in the initialization, will instantiate its PeerAccess class, and pass it to the OKManager calling the method setPeerAccess(). The manager will compare the requests of OKCs requiring access to the peer with the exposed methods in the PeerClass. OKCs whose requirements do not match what the Peer provides cannot be added to the OKC storage. By comparing the OKC requirements with the PeerAccess class, the manager generates an adaptor, that is then passed to every instantiated OKC.
When an OKC needs to access the peer, it will call the method invokePeer(methodName, Argument...args):
try {
invokePeer("doSomething", new ArgumentImpl("name","testname"));
} catch (AdaptorException e) {
e.printStackTrace();
}
Important Notice
One important element to notice is that the Arguments passed in the OKC methods are mapped to constraint parameters, while the arguments passed to the peer methods are mapped to the parameters in the peer’s methods: therefore it is not possible to use the arguments received in the OKC method in peer’s methods invocation.
The following example is wrong:
@MethodSemantic(language="tag", args={"word"})
public boolean okcMethod(Argument a){
try {
invokePeer("doSomething", a);
} catch (AdaptorException e) {
e.printStackTrace();
}
}If the argument is an input argument, the correct solution is:
public boolean okcMethod(Argument a){
try {
invokePeer("doSomething", new ArgumentImp("word",a.getValue()));
} catch (AdaptorException e) {
e.printStackTrace();
}
}
If the argument is an output argument, the correct solution is:
public boolean okcMethod(Argument a){
try {
Argument lw = new ArgumentImp("word");
invokePeer("doSomething", lw);
a.setValue(lw.getValue());
} catch (AdaptorException e) {
e.printStackTrace();
}
}
Specific case: starting a new IM from another IM
An interesting possibility for an OKC is to ask the peer to start a new IM. Here is a simple implementation that exploits the InteractionTask class.
In the Peer access class:
class MyPeerAccess implements PeerAccess {
protected Peer peer;
public MyPeerAccess(Peer p){ peer=p; }
...
@MethodSemantic(language="tag", args={"role_name","interaction_description"})
public boolean attemptIM(Argument rn, Argument imd){
InteractionTask task = new InteractionTask(this.peer, rn.getValue(), AcceptPolicy.ACCEPT_ONE, "", false);
String[] q = (String[]) imd.getValue();
task.attempt(q);
}
...
}To call the method in peer from an OKC:
@RequiredPeerAccess(
methods={"attemptIM(role,description)"}
)
public class MyOKC extends OKCFacadeImpl {
public void someConstraint(...){
try{
invoke("attemptIM", new ArgumentImpl("role", "buyer"), new ArgumentImpl("description","purchase")));
} catch (AdaptorException ae){
ae.printStackTrace();
}
}
}
