130 Coordinator Service Specification

130.1 Introduction

The OSGi programming model is based on the collaboration of standard and custom components. In such a model there is no central authority that has global knowledge of the complete application. Though this lack of authority can significantly increase reusability (and robustness) there are times when the activities of the collaborators must be coordinated. For example, a service that is repeatedly called in a task could optimize performance by caching intermediate results until it knew the task was ended.

To know when a task involving multiple collaborators has ended is the primary purpose of the Coordinator service specification. The Coordinator service provides a rendezvous for an initiator to create a Coordination where collaborators can decide to participate. When the Coordination has ended, all participants are informed.

This Coordinator service provides an explicit Coordination model, the Coordination is explicitly passed as a parameter, and an implicit model where the Coordination is associated with the current thread. Implicit Coordinations can be nested.

Coordinators share the coordination aspects of the resource model of transactions. However, the model is much lighter-weight because it does not support any of the ACID properties.

130.1.1 Essentials

  • Coordination - Provide a solution to allow multiple collaborators to coordinate the outcome of a task initiated by an initiator.

  • Initiator - An initiator must be able to initiate a coordination and control the final outcome.

  • Participants - Participants in the task must be informed when the coordination has ended or failed as well as being able to terminate the Coordination.

  • Time-out - A Coordination should fail after a given time-out.

  • Blocking - Provide support for blocking and serializing access to Participants.

  • Nesting - It must be possible to nest Coordinations.

  • Per Thread Model - Provide a per-thread current Coordination model.

  • Variables - Provide a variable space per Coordination

130.1.2 Entities

  • Coordinator - A service that can create and enumerate Coordinations.

  • Coordination - Represents the ongoing Coordination.

  • Initiator - The party that initiates a Coordination.

  • Participant - A party that wants to be informed of the outcome of a Coordination.

  • Collaborator - Either a participant or initiator.

Figure 130.1 Class and Service overview

Class and Service overview

130.2 Usage

This section is an introduction in the usage of the Coordinator service. It is not the formal specification, the normative part starts at Coordinator Service. This section leaves out some of the details for clarity.

130.2.1 Synopsis

The Coordinator service provides a mechanism for multiple parties to collaborate on a common task without a priori knowledge of who will collaborate in that task. A collaborator can participate by adding a Participant to the Coordination. The Coordination will notify the Participants when the coordination is ended or when it is failed.

Each Coordination has an initiator that creates the Coordination object through the Coordinator service. The initiator can then push this object on a thread-local stack to make it an implicit Coordination or it can pass this object around as a parameter for explicit Coordinations. Collaborators can then use the current Coordination on the stack or get it from a parameter. Whenever a bundle wants to participate in the Coordination it adds itself to the Coordination as a participant. If necessary, a collaborator can initiate a new Coordination, which could be a nested Coordination for implicit Coordinations.

A Coordination must be terminated. Termination is either a normal end when the initiator calls the end method or it is failed when the fail method is called. A Coordination can be failed by any of the collaborators. A Coordination can also fail independently due to a time-out or when the initiator releases its Coordinator service. All participants in the Coordination are informed in reverse participation order about the outcome in a callback for ended or failed Coordinations.

A typical action diagram with a successful outcome is depicted in Figure 130.2.

Figure 130.2 Action Diagram Implicit Coordination

Action Diagram Implicit Coordination

130.2.2 Explicit Coordination

The general pattern for an initiator is to create a Coordination through the Coordinator service, perform the work in a try block, catch any exceptions and fail the Coordination in the catch block, and then ensure ending the Coordination in the finally block. The finally block can cause an exception. This is demonstrated in the following example:

Coordination c = coordinator.create("com.example.work",0);
try {
    doWork(c);
} catch( Exception e ) { 
    c.fail(e); 
} finally { 
    c.end();   
}

This deceptively small template is quite robust:

  • If the doWork method throws an Exception then the template fails with a Coordination Exception because it is failed in the try block.

  • Any exceptions thrown in the try block are automatically causing the Coordination to fail.

  • The Coordination is always terminated and removed from the stack due to the finally block.

  • All failure paths, Coordinations that are failed by any of the collaborators, time-outs, or other problems are handled by the end method in the finally block. It will throw a FAILED or PARTIALLY_ENDED Coordination Exception for any of the failures.

The different failure paths and their handling is pictured in Figure 130.3.

Figure 130.3 Flow through the Coordination template

Flow through the Coordination template

The example shows an explicit Coordination because the create method is used, implicit Coordinations are used in Implicit Coordinations. The parameters of the create method are the name of the Coordination and its time-out. The name is used for informational purposes as well as security. For security reasons, the name must follow the same syntax as the Bundle Symbolic Name. In a secure environment the name can be used to limit Coordinations to a limited set of bundles. For example, a set of bundles signed by a specific signer can use names like com.acme.* that are denied to all other bundles.

The zero time-out specifies that the Coordination will not have a time-out. Otherwise it must be a positive long, indicating the number of milliseconds the Coordination may take. However, implementations should have a configurable time-out to ensure that the system remains alive.

In the doWork method the real work is done in conjunction with the collaborators. Explicit Coordinations can be passed to other threads if needed. Collaborators can decide to add participants whenever they require a notification when the Coordination has been terminated. For example, the following code could be called from the doWork method:

void foo(Coordination c) {
  doPrepare();
  c.addParticipant(this);
}

This method does the preparation work but does not finalize it so that next time it can use some intermediate results. For example, the prepare method could cache a connection to a database that should be reused during the Coordination. The collaborator can assume that it will be called back on either the failed or ended method. These methods could look like:

public void ended(Coordination c)  { doFinish(); }
public void failed(Coordination c) { doFailed(); }

The Coordinator provides the guarantee that this code will always call the doFinish method when the Coordination succeeds and doFailed method when it failed.

The Participant must be aware that the ended(Coordination) and failed(Coordination) methods can be called on any thread.

If the doWork method throws an exception it will end up in the catch block of the initiator. The catch block will then fail the Coordination by calling the fail method with the given exception. If the Coordination was already terminated because something else already had failed it then the method call is ignored, only the first fail is used, later fails are ignored.

In all cases, the finally block is executed last. The finally block ends the Coordination. If this coordination was failed then it will throw a Coordination Exception detailing the reason of the failure. Otherwise it will terminate it and notify all the participants.

The Coordination Exception is a Runtime Exception making it unnecessary to declare it.

130.2.3 Multi Threading

Explicit Coordinations allow the Coordination objects to be passed to many different collaborators who can perform the work on different threads. Each collaborator can fail the Coordination at any moment in time or the time-out can occur on yet another thread. Participants must therefore be aware that the callbacks ended and failed can happen on any thread. The following example shows a typical case where a task is parallelized. If any thread fails the Coordination, all other threads could be notified before they're finished.

Executor executor = ...
final CountDownLatch latch = new CountdownLatch(10);
final Coordination c = coordinator.create("parallel", 0); 
for ( int i=0; i<10; i++) {
  executor.execute(
    new Runnable() {
        public void run() { baz(c); latch.countDown(); }
      });
  }
  latch.await();
  c.end();

The Coordination object is thread safe so it can be freely passed around.

130.2.4 Implicit Coordinations

An explicit Coordination requires that the Coordination is passed as a parameter to the doWork method. The Coordinator also supports implicit Coordinations. With implicit Coordinations the Coordinator maintains a thread local stack of Coordinations where the top of this stack is the current Coordination for that thread. The usage of the implicit Coordination is almost identical to the explicit Coordinations except that all the work occurs on a single thread. The control flow is almost identical to explicit Coordinations:

Coordination c = coordinator.begin("com.example.work",0);
try {
    doWork();
} catch( Exception e ) { 
    c.fail(e); 
} finally { 
    c.end();   
}

See also Figure 130.3. However, in this case the finally block with the call to the end method is even more important. With an implicit Coordination the Coordination is put on a thread local stack in the begin method and must therefore be popped when the Coordination is finished. The finally block ensures therefore the proper cleanup of this thread local stack.

The difference between implicit and explicit Coordinations is that the implicit Coordination is not passed as a parameter, instead, collaborators use the current Coordination. With implicit Coordinations all method invocations in a thread can always access the current Coordination, even if they have many intermediates on the stack. The implicit model allows a collaborator many levels down the stack to detect a current Coordination and register itself without the need to modify all intermediate methods to contain a Coordination parameter. The explicit model has the advantage of explicitness but requires all APIs to be modified to hold the parameter. This model does not support passing the parameter through layers that are not aware of the Coordination. For example, OSGi services in general do not have a Coordination parameter in their methods making the use of explicit Coordinations impossible.

Collaborators can act differently in the presence of a current Coordination. For example, a collaborator can optimize its work flow depending on the presence of a current Coordination.

Coordinator coordinator = ...
void foo() {
  doPrepare();
  if ( !coordinator.addParticipant(this))
      doFinish();
}

The Coordinator service has an addParticipant method that makes working with the current Coordination simple. If there is a current Coordination then the Coordinator service will add the participant and return true, otherwise it returns false. It is therefore easy to react differently in the presence of a current Coordination. In the previous example, the doFinish method will be called immediately if there was no current Coordination, otherwise it is delayed until the Coordination fails or succeeds. The participant callbacks look the same as in the previous section:

public void ended(Coordination c)  { doFinish(); }
public void failed(Coordination c) { doFailed(); }

Though the code looks very similar for the implicit and explicit Coordinations there are some additional rules for implicit Coordinations.

The end method must be called on the same thread as the begin method, trying to end it on another thread results in a WRONG_THREAD Coordination Exception being thrown.

Even though the end method must be called on the initiating thread, the callbacks to the Participants can be done on any thread as the specification allows the Coordinator to use multiple threads for all callbacks.

130.2.5 Partial Ending

The Coordination is a best effort mechanism to coordinate, not a transaction model with integrity guarantees. This means that users of the Coordinator service must understand that there are cases where a Coordination ends in limbo. This happens when one of the Participants throws an Exception in the ended callback. This is similar to a transactional resource manager failing to commit in a 2-phase commit after it has voted yes in the prepare phase; a problem that is the cause of much of the complexity of a transaction manager. The Coordinator is limited to use cases that do not require full ACID properties and can therefore be much simpler. However, users of the Coordinator service must be aware of this limitation.

If a Participant throws an exception in the ended method, the end method that terminated the Coordination must throw a PARTIALLY_ENDED Coordination Exception. It is then up to the initiator to correct the situations. In most cases, this means allowing the exception to be re-thrown and handle the failure at the top level. Handling in those cases usually implies logging and continuing.

The following code shows how the PARTIALLY_ENDED case can be handled more explicitly.

Coordination c = coordinator.begin("work",0);
try {
  doWork();
} catch( Excption e ) {
  c.fail(e);
} finally {
  try {
    c.end();
  } catch( CoordinationException e ) {
    if ( e.getType() == CoordinationException.PARTIALLY_ENDED) {
      // limbo!
      ...
    }
  }
}

130.2.6 Locking

To participate in a Coordination and receive callbacks a collaborator must add a Participant object to the Coordination. The addParticipant(Participant) method blocks if the given Participant object is already used in another Coordination. This blocking facility can be used to implement a number of simple locking schemes that can simplify maintaining state in a concurrent environment.

Using the Participant object as the key for the lock makes it simple to do course grained locking. For example, a service implementation could use the service object as a lock, effectively serializing access to this service when it is used in a Coordination. Coarse grained locking allows all the state to be maintained in the coarse object and not having to worry about multiplexing simultaneous requests. The following code uses the coarse locking pattern because the collaborator implements the Participant interface itself:

public class Collaborator implements Participant{
  public void doWork(Coordination coordination ) {
    ...
    coordination.addParticipant(this);
  }

  public void ended(Coordination c) { ... }
  public void failed(Coordination c) { ... }
}

The simplicity of the coarse grained locking is at the expense of lower performance because tasks are serialized even if it would have no contention. Locks can therefore also be made more fine grained, allowing more concurrency. In the extreme case, creating a new object for each participation makes it possible to never lock. For example, the following code never locks because it always creates a new object for the Participant:

    public void doWork(Coordination coordination){
      final State state = ...
      coordination.addParticipant(
         new Participant() {
           public void ended(Coordination c) { state ... }
           public void failed(Coordination c) { state ...}
    } ); }

130.2.7 Failing

Any collaborator can fail an ongoing Coordination by calling the fail(Throwable) method, the Throwable parameter must not be null. When the Coordination has already terminated then this is a no-op. The Coordinator service has a convenience method that fails the current Coordination if present. The fail methods return a boolean that is true when the method call causes the termination of the Coordination, in all other cases it is false.

Failing a Coordination will immediately perform the callbacks and reject any additional Participants by throwing an ALREADY_ENDED Coordination Exception. The asynchronous nature of the fail method implies that it is possible to have been called even before the addParticipant(Participant) method has returned. Anybody that has the Coordination object can check the failed state with the getFailure() method.

In general, the best and most robust strategy to handle failures is to throw an Exception from the collaborator, allowing the initiator to catch the exception and properly fail the Coordination.

130.2.8 Time-out

The time-out is specified in the Coordinator create(String,long) or begin(String,long) methods. A time-out of zero is indefinite, otherwise the time-out specifies the number of milliseconds the Coordination can take to terminate. A given time-out can be extended with the extendTimeout(long) method. This method will add an additional time-out to the existing deadline if a prior deadline was set. For example, the following code extends the time-out with 5 seconds whenever a message must be sent to a remote address:

Object sendMessage(Message m) {
  Coordination c = coordinator.peek();
  Address a = m.getDestination();
  if ( c != null && a.isRemote() ) {
    c.extendTimeout(5000);
  }
  return sendMessage0(m);
}

Applications should not rely on the exact time-out of the Coordination and only use it as a safety function against deadlocks and hanging collaborators.

130.2.9 Joining

When a Coordination is terminated it is not yet completely finished, the callback to the Participants happens after the atomic termination. In certain cases it is necessary to ensure that a method does not progress until all the participants have been notified. It is therefore possible to wait for the Coordination to completely finish with the join(long) method. This method can have a time-out. For example:

void collaborate( final Coordination c ) {
  doWork();
  Thread t = new Thread() {
    public void run(){
      try {
          c.join(0);
          ... // really terminated here, all participantscalled back
      } catch( Exception e) { ... }
    }
  };
  t.start();
}

130.2.10 Variables

A Participant is likely to have to maintain state that is particular for the collaboration. This state is usually needed in the ended method to properly finalize the work. In general, the best place to store this state is in the Participant object itself, inner classes and final variables are a good technique for storing the state. However, the state can also be stored in a Coordination variable. Each Coordination has a private set of variables that can be obtained with the getVariables() method. The resulting map takes a class as the key and returns an Object. The map is not synchronized, any changes to the map must be synchronized on the returned Map object to ensure the visibility of the changes to other threads. The class used for the key is not related to the returned type, it is a Class object to provide a convenient namespace.

The following example shows how the state can be stored with variables.

public void doWork(Coordination coordination){
  Map<Class<?>,Object> map = coordination.getVariables();
  synchronized(map) {
    State state = (State) map.get( SharedWorker.class );
    if ( state == null ) {
      state = new State(this);
      map.put( state );
      ... do initial work
    }
  }
  ... do other work
  coordination.addParticipant( this );
}
public void ended(Coordination c) {
  Map<Class<?>,Object> map = coordination.getVariables();
  synchronized(map) {
    State state = (State) map.get( SharedWorker.class );
    .. finalize
  }  
}
public void failed(Coordination c) {
  Map<Class<?>,Object> map = coordination.getVariables();
  synchronized(map) {
    State state = (State) map.get( SharedWorker.class );
    .. finalize
  }
}

130.2.11 Optimizing Example

For example, a web based system has a charge service:

public interface Charge {
  void charge( String reason, int amount );
}

This service is used throughout the system for charging the tasks the system performs. Each servlet request can actually create multiple Charge Data Records (CDR). For this reason, a Coordination is started before the page is constructed. Each part of the page that has an associated cost must create a CDR. There are the following issues at stake:

  • Charging should not take place when failing, and

  • Performance can be optimized to only persist the CDRs once, and

  • The user must be passed to the Charge service.

To begin with the request code:

public void doGet(HttpServletRequest rq, HttpServletResponsersp) {
  Coordination c = coordinator.begin("com.acme.request", 30000);
  try {
    Principal p = rq.getUserPrincipal();
    Map<Class<?>,Object> map = c.getVariables();
    map.put( Principal.class, p );
    buildPage(rq,rsp);
  } catch( Exception e  ) { c.fail(e); } 
    finally               { c.end(); }
}

Each method that has a charge will call the Charge service. The following code shows an implementation of this Charge service.

public class ChargeImpl implements Charge,Participant {
  final List<CDR> records = new ArrayList<CDR>();

  public void charge( String reason, int amount ) {
    Coordination c = coordinator.peek();
    if ( c == null ) {
       save( Arrays.asList( new CDR(null, reason, amount)));
    } else {
      Principal p = getPrincipal(c);
      records.add( new CDR(p, reason, amount ) );
      c.addParticipant( this );
    }
  }

  Principal getPrincipal(Coordination c) {
    if ( c == null )
      return null;

    Map<Class<?>,Object> map = c.getVariables();
    synchronized(map) {
      Principal p = (Principal) map.get( Principal.class );
      return p != null ? p : getPrincipal(c.getEnclosingCoordination());
    }
  }

  public void ended(Coordination c) {
    save(records);
    records.clear();
  }
  public void failed(Coordination c) {
    records.clear();
  }

  void save(List<CDR> records) { ... }
}

130.2.12 Security Example

The Coordination Permission is a filter based permission that is asserted for many of the methods in the API, the bundle that is checked is always the bundle that created the corresponding Coordination. For example:

ALLOW {
     [ BundleSignerCondition "cn=ACME" ]
    ( CoordinationPermission "(signer=cn=ACME)" "*" )
}

This example allows bundles signed by ACME to perform all Coordination actions on Coordinations created by bundles signed by ACME.

The filter can also assert the name of the Coordination:

coordination.name

It is therefore possible to create a name based protection scheme. By denying all bundles except a select group through the use of a name prefix, the use of Coordinations can be restricted to this select group:

DENY {
     [ BundleSignerCondition "cn=ACME" "!" ]
    ( CoordinationPermission "(coordination.name=com.acme.*)""*" )
}
ALLOW {
    ( CoordinationPermission "(coordination.name=*)" "*" )
}

If a bundle is not signed by ACME it will be denied the use of Coordination names starting with com.acme. though it will be allowed to use any other name. This effectively enables only bundles signed by ACME to create Coordinations with this name prefix.

130.3 Coordinator Service

The Coordinator service is the entry point for the Coordination. It provides the following functions:

  • Coordination creation

  • Life cycle management of a Coordination

  • Thread based Coordinations

  • Introspection

130.3.1 Coordination Creation

A Coordination object is created by an initiator. An initiator can create a Coordination object with the Coordinator create(String,long) or begin(String,long) method. Each Coordination when created gets a positive long identity that is available with getId(). Ids are a unique identifier for a specific Coordinator service. The id is always increasing, that is, a Coordination with a higher id is created later.

The create methods specify the name of the Coordination. This name is a security concept, see Security, as well as used for debugging. The coordination name must therefore conform to the same syntax as a bundle symbolic name:

coordination-name ::= symbolic-name   // see OSGi Core Release 8

Passing a name that does not conform to this syntax must throw an Illegal Argument Exception. There are no constraints on duplicates, multiple different Coordinations can use the same name. The name of the Coordination is available with the getName() method.

130.3.2 Adding Participants

The Coordination object can be passed to collaborators as a parameter in a method call. Some of these collaborators might be interested in participating in the given Coordination, they can achieve this by adding a Participant object to the Coordination.

A Participant is a collaborator that requires a callback after the Coordination has been terminated, either when it ended or when it failed. To participate, it must add a Participant object to a Coordination with the addParticipant(Participant) method on Coordination. This method throws an ALREADY_ENDED or FAILED Coordination Exception when the Coordination has been terminated.

When a Participant is:

  • Not in any Coordination - Add it to the given Coordination and return.

  • In target Coordination - Ignore, participant is already present. A Participant can participate in the same Coordination multiple times by calling addParticipant(Participant) but will only be called back once when the Coordination is terminated. Its order must be defined by the first addition.

  • In another Coordination - Lock until after the other Coordination has notified all the Participants. Implementations can detect deadlocks in certain cases and throw a Coordination Exception if a dead lock exist, otherwise the deadlock is solved when the Coordination times out.

Verifying if a Participant object is already in another Coordination must use identity and not equality.

130.3.3 Active

A Coordination is active until it is terminated. A Coordination can terminate because it is ended, or it is failed. The following methods cause a termination:

  • end() - A normal end. All participants that were added before the end call are called back on their ended(Coordination) method.

  • fail(Throwable) - The Coordination has failed, this will call back the failed(Coordination) method on the participants. This method can be called by the Coordinator, the initiator, or any of the collaborators. There are a number of failures that are built in to the Coordinator. These failures use singleton Exception instances defined in the Coordination interface:

    • TIMEOUT - If the Coordination times out the Coordination is failed with the TIMEOUT exception instance in Coordination.

    • RELEASED - If the Coordinator that created the Coordination was unget, all Coordinations created by it will fail with the RELEASED exception.

The state diagram for the Coordination is pictured in Figure 130.4.

Figure 130.4 Coordination state diagram

Coordination state diagram

130.3.4 Explicit and Implicit Models

The Coordinator supports two very different models of usage: explicit and implicit. The explicit model is when a Coordination is created and passed around as a parameter. The second model is the implicit model where the Coordinator maintains a thread local stack of Coordinations. Any collaborator can then decide to use the top of the stack as the current Coordination. The peek() method provides access to the current Coordination.

The begin(String,long) method creates a new Coordination and pushes this on the stack, beginning an implicit Coordination. This is identical to:

coordinator.create("work",0).push();

Once a Coordination is pushed on a stack it is from that moment on associated with the current thread. A Coordination can only be pushed once, the ALREADY_PUSHED Coordination Exception must be thrown when the Coordination is already associated with one of the thread local stacks maintained by the Coordinator service.

The Coordination is removed from the stack in the end() method. The end() method must not only terminate itself but it must also terminate all nested Coordinations.

The current Coordination can also be explicitly removed with the Coordinator pop() method.

A Coordination that is pushed on a thread local stack returns the associated thread on the getThread() method. This method returns null for Coordinations not on any stack, that is, explicit Coordinations.

130.3.5 Termination

Both the end() and fail(Throwable) methods terminate the Coordination if it was not already terminated. Termination is atomic, only the end or the fail method can terminate the Coordination. Though this happens on different threads, a Coordination can never both end and fail from any perspective. That is, if a fail races with end then only one of them can win and the other provides the feedback that the Coordination was already terminated.

Terminating a Coordination has the following effects:

  • It is atomic, it can only happen once in a Coordination

  • It freezes the set of participants, no more participants can be added

130.3.6 Ending

The end() method should always be called at the end of a Coordination to ensure proper termination, notification, and cleanup. The end method throws a FAILED or PARTIALLY_ENDED Coordination Exception if the Coordination was failed before.

If the Coordination had already been ended before then this is a programming error and an ALREADY_ENDED Configuration Exception is thrown. The end() method should never be called twice on the same Coordination.

If the termination succeeds then the participants must be notified by calling the ended(Coordination) method on each Participant that had been successfully added to the Coordination. This callback can take place on any thread but must be in reverse order of adding. That is, the last added Participant is called back first.

Participants must never make any assumptions about the current Coordination in the callback. The Coordination it was added to is therefore given as an explicit parameter in the ended(Coordination) method.

If a Participant throws an Exception then this must not prevent the calling of the remaining participants. The Exception should be logged. If a Participant has thrown an Exception then the end() method must throw a PARTIALLY_ENDED Coordination Exception after the last Participant has returned from its callback, otherwise the method returns normally. Participants should normally not throw Exceptions in their callbacks.

If the Coordination is implicit (it is pushed on a stack) then the Coordination must be removed from its stack after the participants have been called back. This requires that the ending thread is the same as the thread of the Coordination. The end thread is the thread of the end() method call. If the Coordination's thread is not the same as the ending thread then a WRONG_THREAD Coordination Exception is thrown.

If the ending Coordination is on the stack but it is not the current Coordination then each nested Coordination must be ended before the current Coordination, see Nesting Implicit Coordinations for more information.

The fail(Throwable) method must not remove the current Coordination, it must remain on the stack. The initiator must always call the end() method. Always calling end() in a finally block is therefore paramount.

130.3.7 Failing, TIMEOUT, ORPHANED, and RELEASED

Failing can happen asynchronously during the time a Coordination is active. A Coordination is failed by calling fail(Throwable). The Throwable argument must not be null, it is the cause of the failure.

Failing a Coordination must first terminate it. If the Coordination was already terminated the fail(Throwable) method has no effect. Otherwise, it must callback all its added Participants on the failed(Coordination) callback method. Exceptions thrown from this method should be logged and further ignored. The callback can occur on any thread, including the caller's.

Implicit Coordinations must not be popped from its stack in a fail nor is it necessary to call the fail method from any particular thread. The removal of the Coordination from the stack must happen in the end method.

There are two asynchronous events that can also fail the Coordination. If the Coordination times out, it will be treated as a fail( TIMEOUT ) and if the Coordinator is ungotten with active Coordinations then each of those Coordinations must fail as if fail( RELEASED ) is called.

A Coordination can also be orphaned. An orphaned Coordination has no longer any outside references. This means that the Coordination can no longer be ended or failed. Such Coordinations must fail with an ORPHANED Exception.

130.3.8 Nesting Implicit Coordinations

Implicit Coordinations can be nested. For this reason, the Coordinator maintains a thread local stack of Coordinations where the top, accessible with the peek() method, is the current Coordination. Each time a new Coordination is begun with the begin(String,long) method, the current Coordination is replaced with the newly created Coordination. When that Coordination is ended, the previous current Coordination is restored. Nesting is always on the same thread, implicit Coordinations are always associated with a single thread, available through its getThread() method. The end method must be called on the same thread as the begin(String,long) or last push() method.

Using the standard model for implicit Coordinations, where the initiator always ends the Coordination on the same thread as it begun, ensures that nesting is properly handled. However, in certain cases it is necessary to manipulate the stack or make implicit Coordinations explicit or vice versa. For this reason, it is possible to pop Coordinations from the stack with the pop() method. This method disassociates the Coordination from the current thread and restores the previous (if any) Coordination as the current Thread. A Coordination can then be made the current Coordination for a thread by calling the push() method. However, a Coordination can be pushed on the stack at most once. If a Coordination is pushed a second time, in any thread, the ALREADY_PUSHED Coordination Exception must be thrown.

The Coordination is removed from its stack when the end() method is called. It is therefore highly recommended to always end a Coordination in the nesting order. However, it is possible that a Coordination is ended that is not the current Coordination, it has nested Coordinations that were not properly ended. In that case all nested Coordinations must be ended in reverse creation order, that is, the current Coordination first, by calling the end method on it.

If any Coordination fails to end properly (including PARTIALLY_ENDED ) then the remaining Coordinations on the stack must fail and chain the exceptions. In pseudo code:

while (coordinator.peek() != this) {
 try {
     coordinator.peek().end();
 } catch (CoordinationException e) {
      coordinator.peek().fail(e);
 }
}

130.3.9 Time-outs

When a Coordination is created it will receive a time-out. A time-out is a positive value or zero. A zero value indicates that the Coordination should have no time-out. This does not imply that a Coordination will never time-out, implementations are allowed to be configured with a limit to the maximum active time for a Coordination.

Collaborators can extend the time out with the extendTimeout(long) method. If no time-out was set (0), this method will be ignored. Otherwise the given amount (which must be positive) is added to the existing deadline. A Coordinator implementation can fail the Coordination earlier, however, when configured to do so.

If a Coordination is timed out, the Coordination is failed with a fail(TIMEOUT) method call from an unspecified thread, see Failing, TIMEOUT, ORPHANED, and RELEASED.

130.3.10 Released

The Coordination's life cycle is bound to the Coordinator service that created it. If the initiator's bundle ungets this service then the Coordinator must fail all the Coordinations created by this Coordinator by calling the fail(RELEASED) method.

Participants from bundles that are stopped are not taken into account. This means that it is possible that a participant is called while its bundle is stopped. Stopped Participants should fail any Coordinations that they participate in.

130.3.11 Coordinator Convenience Methods

The Coordinator contains a number of convenience methods that can be used by collaborators to interact with the current Coordination.

  • begin(String,long) - Is logically the same as create(String,long). push().

  • addParticipant(Participant) - This method makes it easy to react differently to the presence of a current implicit Coordination. If a current Coordination exists, the participant is added and true is returned (or an exception thrown if the Coordination is already terminated), otherwise false is returned.

  • fail(Throwable) - If there is no current Coordination, this method returns false. Otherwise it returns the result of calling fail(Throwable) on the current Coordination. This method therefore only returns true when a current Coordination was actually terminated due to this call.

130.3.12 Administrative Access

The Coordination objects provide a number of methods that are used for administrating the Coordinations and the Coordinator.

  • getBundle() - Provide the bundle that created the Coordination. This bundle is the bundle belonging to the Bundle Context used to get the Coordinator service.

  • getFailure() - The Exception that caused this Coordination to fail or null. There are two fixed exception instances for a time out ( TIMEOUT ), when the Coordination is orphaned ( ORPHANED ), and when the Coordinator service is released ( RELEASED ).

  • getId() - The Coordination's id.

  • getName() - The name of the Coordination.

  • getParticipants() - The current list of participants. This is a mutable snapshot of the added participants. Changing the snapshot has no effect on the Coordination.

  • getThread() - Answer the thread associated with an implicit Coordination. If the Coordination is not implicit then the answer is null.

  • getEnclosingCoordination() - Return the enclosing Coordination.

And for the Coordinator:

130.3.13 Summary

A Coordination can exist in three different states ACTIVE, END, and FAIL. During its life it will transition from ACTIVE to either END or FAIL. The entry (when the state is entered) and exit (when the state is left) actions when this transition takes place and the effect on the different methods are summarized in the following table.

Table 130.1 States and transitions

State/Method ACTIVE END FAIL

entry action

Notify all the participants by calling the ended(Coordination) method.

Notify all the participants by calling the failed(Coordination) method.

exit action

Terminate

end()

-> END.

Can throw PARTIALLY_ENDED

throws ALREADY_ENDED

throws FAILED

fail(Throwable)

-> FAIL, return true.

return false.

return false.


130.4 Security

This specification provides a Coordination Permission. This permission can enforce the name of the coordination as well as assert the properties of the initiating bundle, like for example the signer or bundle symbolic name. The permission therefore uses a filter as name, as defined in the filter based permissions section in OSGi Core Release 8, see OSGi Core Release 8. There is one additional parameter for the filter:

coordination.name

The value is the given name of the Coordination. Restricting the name of a Coordination allows the deployer to limit the use of this name to a restricted set of bundles.

The following actions are defined:

  • INITIATE - Required to initiate and control a Coordination.

  • PARTICIPATE - Required to participate in a Coordination.

  • ADMIN - Required to administrate a Coordinator.

The target bundle of the Coordination Permission is the initiator's bundle. This is the bundle that got the Coordinator service to create the Coordination. An initiator must therefore have permission to create Coordinations for itself.

There are two constructors available:

130.5 org.osgi.service.coordinator

Version 1.0

Coordinator Package Version 1.0.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest. This package has two types of users: the consumers that use the API in this package and the providers that implement the API in this package.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.coordinator; version="[1.0,2.0)"

Example import for providers implementing the API in this package:

Import-Package: org.osgi.service.coordinator; version="[1.0,1.1)"

130.5.1 Summary

130.5.2 public interface Coordination

A Coordination object is used to coordinate a number of independent Participants.

Once a Coordination is created, it can be used to add Participant objects. When the Coordination is ended, the participants are notified. A Coordination can also fail for various reasons. When this occurs, the participants are notified of the failure.

A Coordination must be in one of two states, either ACTIVE or TERMINATED. The transition between ACTIVE and TERMINATED must be atomic, ensuring that a Participant can be guaranteed of either receiving an exception when adding itself to a Coordination or of receiving notification the Coordination has terminated.

A Coordination object is thread safe and can be passed as a parameter to other parties regardless of the threads these parties use.

The following example code shows how a Coordination should be used.

 void foo() {
   Coordination c = coordinator.create("work", 0);
   try {
     doWork(c);
   }
   catch (Exception e) {
     c.fail(e);
   }
   finally {
     c.end();
   }
 }

Thread-safe

Consumers of this API must not implement this type

130.5.2.1 public static final Exception ORPHANED

A singleton exception that will be the failure cause when a Coordination has been orphaned.

130.5.2.2 public static final Exception RELEASED

A singleton exception that will be the failure cause when the Coordinations created by a bundle are terminated because the bundle released the Coordinator service.

130.5.2.3 public static final Exception TIMEOUT

A singleton exception that will be the failure cause when a Coordination times out.

130.5.2.4 public void addParticipant(Participant participant)

The Participant to register with this Coordination. The participant must not be null.

Register a Participant with this Coordination.

Once a Participant is registered with this Coordination, it is guaranteed to receive a notification for either normal or failure termination when this Coordination is terminated.

Participants are registered using their object identity. Once a Participant is registered with this Coordination, subsequent attempts to register the Participant again with this Coordination are ignored and the Participant is only notified once when this Coordination is terminated.

A Participant can only be registered with a single active Coordination at a time. If a Participant is already registered with an active Coordination, attempts to register the Participation with another active Coordination will block until the Coordination the Participant is registered with terminates. Notice that in edge cases the notification to the Participant that this Coordination has terminated can happen before this method returns.

Attempting to register a Participant with a terminated Coordination will result in a CoordinationException being thrown.

The ordering of notifying Participants must follow the reverse order in which the Participants were registered.

CoordinationException– If the Participant could not be registered with this Coordination. This exception should normally not be caught by the caller but allowed to be caught by the initiator of this Coordination.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for this Coordination.

130.5.2.5 public void end()

Terminate this Coordination normally.

If this Coordination has been pushed on the thread local Coordination stack of another thread, this method does nothing except throw a CoordinationException of type CoordinationException.WRONG_THREAD.

If this Coordination has been pushed on the thread local Coordination stack of this thread but is not the current Coordination, then the Coordinations on the thread local Coordination stack above this Coordination must be terminated and removed from the thread local Coordination stack before this Coordination is terminated. Each of these Coordinations, starting with the current Coordination, will be terminated normally . If the termination throws a CoordinationException, then the next Coordination on the thread local Coordination stack will be terminated as a failure with a failure cause of the thrown CoordinationException. At the end of this process, this Coordination will be the current Coordination and will have been terminated as a failure if any of the terminated Coordinations threw a CoordinationException

If this Coordination is the current Coordination, then it will be removed from the thread local Coordination stack.

If this Coordination is already terminated, a CoordinationException is thrown. If this Coordination was terminated as a failure, the failure cause will be the cause of the thrown CoordinationException.

Otherwise, this Coordination is terminated normally and then all registered Participants are notified. Participants should finalize any work associated with this Coordination. The successful return of this method indicates that the Coordination has terminated normally and all registered Participants have been notified of the normal termination.

It is possible that one of the Participants throws an exception during notification. If this happens, this Coordination is considered to have partially failed and this method must throw a CoordinationException of type CoordinationException.PARTIALLY_ENDED after all the registered Participants have been notified.

CoordinationException– If this Coordination has failed, including timed out, or partially failed or this Coordination is on the thread local Coordination stack of another thread.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for this Coordination.

130.5.2.6 public long extendTimeout(long timeMillis)

The time in milliseconds to extend the current timeout. If the initial timeout was specified as 0, no extension must take place. A zero must have no effect.

Extend the time out of this Coordination.

Participants can call this method to extend the timeout of this Coordination with at least the specified time. This can be done by Participants when they know a task will take more than normal time.

This method will return the new deadline if an extension took place or the current deadline if, for whatever reason, no extension takes place. Note that if a maximum timeout is in effect, the deadline may not be extended by as much as was requested, if at all. If there is no deadline, zero is returned. Specifying a timeout extension of 0 will return the existing deadline.

The new deadline in milliseconds. If the specified time is 0, the existing deadline is returned. If this Coordination was created with an initial timeout of 0, no timeout is set and 0 is returned.

CoordinationException– If this Coordination is terminated.

IllegalArgumentException– If the specified time is negative.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for this Coordination.

130.5.2.7 public boolean fail(Throwable cause)

The failure cause. The failure cause must not be null.

Terminate this Coordination as a failure with the specified failure cause.

If this Coordination is already terminated, this method does nothing and returns false.

Otherwise, this Coordination is terminated as a failure with the specified failure cause and then all registered Participants are notified. Participants should discard any work associated with this Coordination. This method will return true.

If this Coordination has been pushed onto a thread local Coordination stack, this Coordination is not removed from the stack. The creator of this Coordination must still call end() on this Coordination to cause it to be removed from the thread local Coordination stack.

true if this Coordination was active and was terminated by this method, otherwise false.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for this Coordination.

130.5.2.8 public Bundle getBundle()

Returns the bundle that created this Coordination. This is the bundle that obtained the Coordinator service that was used to create this Coordination.

The bundle that created this Coordination.

SecurityException– If the caller does not have CoordinationPermission[ADMIN] for this Coordination.

130.5.2.9 public Coordination getEnclosingCoordination()

Returns the Coordination enclosing this Coordination if this Coordination is on the thread local Coordination stack.

When a Coordination is pushed onto the thread local Coordination stack, the former current Coordination, if any, is the enclosing Coordination of this Coordination. When this Coordination is removed from the thread local Coordination stack, this Coordination no longer has an enclosing Coordination.

The Coordination enclosing this Coordination if this Coordination is on the thread local Coordination stack or null if this Coordination is not on the thread local Coordination stack or has no enclosing Coordination.

SecurityException– If the caller does not have CoordinationPermission[ADMIN] for this Coordination.

130.5.2.10 public Throwable getFailure()

Returns the failure cause of this Coordination.

If this Coordination has failed, then this method will return the failure cause.

If this Coordination timed out, this method will return TIMEOUT as the failure cause. If this Coordination was active when the bundle that created it released the Coordinator service, this method will return RELEASED as the failure cause. If the Coordination was orphaned, this method will return ORPHANED as the failure cause.

The failure cause of this Coordination or null if this Coordination has not terminated as a failure.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for this Coordination.

130.5.2.11 public long getId()

Returns the id assigned to this Coordination. The id is assigned by the Coordinator service which created this Coordination and is unique among all the Coordinations created by the Coordinator service and must not be reused as long as the Coordinator service remains registered. The id must be positive and monotonically increases for each Coordination created by the Coordinator service.

The id assigned to this Coordination.

130.5.2.12 public String getName()

Returns the name of this Coordination. The name is specified when this Coordination was created.

The name of this Coordination.

130.5.2.13 public List<Participant> getParticipants()

Returns a snapshot of the Participants registered with this Coordination.

A snapshot of the Participants registered with this Coordination. If no Participants are registered with this Coordination, the returned list will be empty. The list is ordered in the order the Participants were registered. The returned list is the property of the caller and can be modified by the caller.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for this Coordination.

130.5.2.14 public Thread getThread()

Returns the thread in whose thread local Coordination stack this Coordination has been pushed.

The thread in whose thread local Coordination stack this Coordination has been pushed or null if this Coordination is not in any thread local Coordination stack.

SecurityException– If the caller does not have CoordinationPermission[ADMIN] for this Coordination.

130.5.2.15 public Map<Class<?>, Object> getVariables()

Returns the variable map associated with this Coordination. Each Coordination has a map that can be used for communicating between different Participants. The key of the map is a class, allowing for private data to be stored in the map by using implementation classes or shared data by using shared interfaces. The returned map is not synchronized. Users of the map must synchronize on the Map object while making changes.

The variable map associated with this Coordination.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPANT] for this Coordination.

130.5.2.16 public boolean isTerminated()

Returns whether this Coordination is terminated.

true if this Coordination is terminated, otherwise false if this Coordination is active.

130.5.2.17 public void join(long timeMillis) throws InterruptedException

Maximum time in milliseconds to wait. Specifying a time of 0 will wait until this Coordination is terminated.

Wait until this Coordination is terminated and all registered Participants have been notified.

InterruptedException– If the wait is interrupted.

IllegalArgumentException– If the specified time is negative.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for this Coordination.

130.5.2.18 public Coordination push()

Push this Coordination object onto the thread local Coordination stack to make it the current Coordination.

This Coordination.

CoordinationException– If this Coordination is already on the any thread's thread local Coordination stack or this Coordination is terminated.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for this Coordination.

130.5.3 public class CoordinationException
extends RuntimeException

Unchecked exception which may be thrown by a Coordinator implementation.

130.5.3.1 public static final int ALREADY_ENDED = 4

The Coordination has already terminated normally.

130.5.3.2 public static final int ALREADY_PUSHED = 5

The Coordination was already on a thread's thread local Coordination stack.

130.5.3.3 public static final int DEADLOCK_DETECTED = 1

Registering a Participant with a Coordination would have resulted in a deadlock.

130.5.3.4 public static final int FAILED = 2

The Coordination has terminated as a failure with Coordination.fail(Throwable). When this exception type is used, the getCause() method must return a non-null value.

130.5.3.5 public static final int LOCK_INTERRUPTED = 6

The current thread was interrupted while waiting to register a Participant with a Coordination.

130.5.3.6 public static final int PARTIALLY_ENDED = 3

The Coordination has partially ended.

130.5.3.7 public static final int UNKNOWN = 0

Unknown reason for this exception.

130.5.3.8 public static final int WRONG_THREAD = 7

The Coordination cannot be ended by the calling thread since the Coordination is on the thread local Coordination stack of another thread.

130.5.3.9 public CoordinationException(String message, Coordination coordination, int type, Throwable cause)

The detail message for this exception.

The Coordination associated with this exception.

The cause associated with this exception.

The type of this exception.

Create a new Coordination Exception with a cause.

IllegalArgumentException– If the specified type is FAILED and the specified cause is null.

130.5.3.10 public CoordinationException(String message, Coordination coordination, int type)

The detail message for this exception.

The Coordination associated with this exception.

The type of this exception.

Create a new Coordination Exception.

IllegalArgumentException– If the specified type is FAILED .

130.5.3.11 public long getId()

Returns the id of the Coordination associated with this exception.

The id of the Coordination associated with this exception or -1 if no Coordination is associated with this exception.

130.5.3.12 public String getName()

Returns the name of the Coordination associated with this exception.

The name of the Coordination associated with this exception or "<>" if no Coordination is associated with this exception.

130.5.3.13 public int getType()

Returns the type for this exception.

The type of this exception.

130.5.4 public final class CoordinationPermission
extends BasicPermission

A bundle's authority to create or use a Coordination.

CoordinationPermission has three actions: initiate, participate and admin.

Thread-safe

130.5.4.1 public static final String ADMIN = "admin"

The action string admin.

130.5.4.2 public static final String INITIATE = "initiate"

The action string initiate.

130.5.4.3 public static final String PARTICIPATE = "participate"

The action string participate.

130.5.4.4 public CoordinationPermission(String filter, String actions)

A filter expression. Filter attribute names are processed in a case sensitive manner. A special value of "*" can be used to match all coordinations.

admin, initiate or participate (canonical order).

Creates a new granted CoordinationPermission object. This constructor must only be used to create a permission that is going to be checked.

Examples:

 (coordination.name=com.acme.*)
 (&(signer=\*,o=ACME,c=US)(coordination.name=com.acme.*))
 (signer=\*,o=ACME,c=US)

When a signer key is used within the filter expression the signer value must escape the special filter chars ('*', '(', ')').

The name is specified as a filter expression. The filter gives access to the following attributes:

  • signer - A Distinguished Name chain used to sign the exporting bundle. Wildcards in a DN are not matched according to the filter string rules, but according to the rules defined for a DN chain.

  • location - The location of the exporting bundle.

  • id - The bundle ID of the exporting bundle.

  • name - The symbolic name of the exporting bundle.

  • coordination.name - The name of the requested coordination.

Filter attribute names are processed in a case sensitive manner.

IllegalArgumentException– If the filter has an invalid syntax.

130.5.4.5 public CoordinationPermission(String coordinationName, Bundle coordinationBundle, String actions)

The name of the requested Coordination.

The bundle which created the requested Coordination.

admin, initiate or participate (canonical order).

Creates a new requested CoordinationPermission object to be used by the code that must perform checkPermission. CoordinationPermission objects created with this constructor cannot be added to an CoordinationPermission permission collection.

130.5.4.6 public boolean equals(Object obj)

The object to test for equality with this CoordinationPermission object.

Determines the equality of two CoordinationPermission objects. This method checks that specified permission has the same name and CoordinationPermission actions as this CoordinationPermission object.

true if obj is a CoordinationPermission, and has the same name and actions as this CoordinationPermission object; false otherwise.

130.5.4.7 public String getActions()

Returns the canonical string representation of the CoordinationPermission actions.

Always returns present CoordinationPermission actions in the following order: admin, initiate, participate.

Canonical string representation of the CoordinationPermission actions.

130.5.4.8 public int hashCode()

Returns the hash code value for this object.

A hash code value for this object.

130.5.4.9 public boolean implies(Permission p)

The requested permission.

Determines if the specified permission is implied by this object.

This method checks that the filter of the target is implied by the coordination name of this object. The list of CoordinationPermission actions must either match or allow for the list of the target object to imply the target CoordinationPermission action.

true if the specified permission is implied by this object; false otherwise.

130.5.4.10 public PermissionCollection newPermissionCollection()

Returns a new PermissionCollection object suitable for storing CoordinationPermission objects.

A new PermissionCollection object.

130.5.5 public interface Coordinator

A Coordinator service coordinates activities between different parties.

A bundle can use the Coordinator service to create Coordination objects. Once a Coordination object is created, it can be pushed on the thread local Coordination stack to be an implicit parameter as the current Coordination for calls to other parties, or it can be passed directly to other parties as an argument. The current Coordination, which is on the top of the current thread's thread local Coordination stack, can be obtained with peek().

Any active Coordinations created by a bundle must be terminated when the bundle releases the Coordinator service. The Coordinator service must fail these Coordinations with the RELEASED exception.

A Participant can register to participate in a Coordination and receive notification of the termination of the Coordination.

The following example code shows a example usage of the Coordinator service.

 void foo() {
   Coordination c = coordinator.begin("work", 0);
   try {
     doWork();
   } catch (Exception e) {
     c.fail(e);
   } finally {
     c.end();
   }
 }

In the doWork method, code can be called that requires notification of the termination of the Coordination. The doWork method can then register a Participant with the Coordination.

 void doWork() {
   if (coordinator.addParticipant(this)) {
     beginWork();
   } else {
     beginWork();
     finishWork();
   }
 }
 
 void ended(Coordination c) {
   finishWork();
 }
 
 void failed(Coordination c) {
   undoWork();
 }

Thread-safe

Consumers of this API must not implement this type

130.5.5.1 public boolean addParticipant(Participant participant)

The Participant to register with the current Coordination. The participant must not be null.

Register a Participant with the current Coordination.

If there is no current Coordination, this method does nothing and returns false.

Otherwise, this method calls Coordination.addParticipant(Participant) with the specified Participant on the current Coordination and returns true.

false if there was no current Coordination, otherwise returns true.

CoordinationException– If the Participant could not be registered with the current Coordination. This exception should normally not be caught by the caller but allowed to be caught by the initiator of this Coordination.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for the current Coordination.

Coordination.addParticipant(Participant)

130.5.5.2 public Coordination begin(String name, long timeMillis)

The name of this coordination. The name does not have to be unique but must follow the symbolic-name syntax from the Core specification.

Timeout in milliseconds. A value of 0 means no timeout is required. If the Coordination is not terminated within the timeout, the Coordinator service will fail the Coordination with a TIMEOUT exception.

Create a new Coordination and make it the current Coordination.

This method does that same thing as calling create(name, timeMillis).push()

A new Coordination object

IllegalArgumentException– If the specified name does not follow the symbolic-name syntax or the specified time is negative.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for the specified name and creating bundle.

130.5.5.3 public Coordination create(String name, long timeMillis)

The name of this coordination. The name does not have to be unique but must follow the symbolic-name syntax from the Core specification.

Timeout in milliseconds. A value of 0 means no timeout is required. If the Coordination is not terminated within the timeout, the Coordinator service will fail the Coordination with a TIMEOUT exception.

Create a new Coordination.

The new Coordination object.

IllegalArgumentException– If the specified name does not follow the symbolic-name syntax or the specified time is negative.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for the specified name and creating bundle.

130.5.5.4 public boolean fail(Throwable cause)

The failure cause. The failure cause must not be null .

Terminate the current Coordination as a failure with the specified failure cause.

If there is no current Coordination, this method does nothing and returns false.

Otherwise, this method returns the result from calling Coordination.fail(Throwable) with the specified failure cause on the current Coordination.

false if there was no current Coordination, otherwise returns the result from calling Coordination.fail(Throwable) on the current Coordination.

SecurityException– If the caller does not have CoordinationPermission[PARTICIPATE] for the current Coordination.

Coordination.fail(Throwable)

130.5.5.5 public Coordination getCoordination(long id)

The id of the requested Coordination.

Returns the Coordination with the specified id.

A Coordination having with specified id or null if no Coordination with the specified id exists, the Coordination with the specified id is terminated or the caller does not have CoordinationPermission[ADMIN] for the Coordination with the specified id.

130.5.5.6 public Collection<Coordination> getCoordinations()

Returns a snapshot of all active Coordinations.

Since Coordinations can be terminated at any time, Coordinations in the returned collection can be terminated before the caller examines the returned collection.

The returned collection must only contain the Coordinations for which the caller has CoordinationPermission[ADMIN].

A snapshot of all active Coordinations. If there are no active Coordinations, the returned list will be empty. The returned collection is the property of the caller and can be modified by the caller.

130.5.5.7 public Coordination peek()

Returns the current Coordination.

The current Coordination is the Coordination at the top of the thread local Coordination stack. If the thread local Coordination stack is empty, there is no current Coordination. Each Coordinator service maintains thread local Coordination stacks.

This method does not alter the thread local Coordination stack.

The current Coordination or null if the thread local Coordination stack is empty.

130.5.5.8 public Coordination pop()

Remove the current Coordination from the thread local Coordination stack.

The current Coordination is the Coordination at the top of the thread local Coordination stack. If the thread local Coordination stack is empty, there is no current Coordination. Each Coordinator service maintains its own thread local Coordination stacks.

This method alters the thread local Coordination stack, if it is not empty, by removing the Coordination at the top of the thread local Coordination stack.

The Coordination that was the current Coordination or null if the thread local Coordination stack is empty.

SecurityException– If the caller does not have CoordinationPermission[INITIATE] for the current Coordination.

130.5.6 public interface Participant

A Participant participates in a Coordination.

A Participant can participate in a Coordination by registering itself with the Coordination. After successfully registering itself, the Participant is notified when the Coordination is terminated.

If a Coordination terminates normally, then all registered Participants are notified on their ended(Coordination) method. If the Coordination terminates as a failure, then all registered Participants are notified on their failed(Coordination) method.

Participants are required to be thread safe as notification can be made on any thread.

A Participant can only be registered with a single active Coordination at a time. If a Participant is already registered with an active Coordination, attempts to register the Participation with another active Coordination will block until the Coordination the Participant is registered with terminates. Notice that in edge cases the notification to the Participant that the Coordination has terminated can happen before the registration method returns.

Thread-safe

130.5.6.1 public void ended(Coordination coordination) throws Exception

The Coordination that has terminated normally.

Notification that a Coordination has terminated normally.

This Participant should finalize any work associated with the specified Coordination.

Exception– If this Participant throws an exception, the Coordinator service should log the exception. The Coordination.end() method which is notifying this Participant must continue notification of other registered Participants. When this is completed, the Coordination.end() method must throw a CoordinationException of type CoordinationException.PARTIALLY_ENDED.

130.5.6.2 public void failed(Coordination coordination) throws Exception

The Coordination that has terminated as a failure.

Notification that a Coordination has terminated as a failure.

This Participant should discard any work associated with the specified Coordination.

Exception– If this Participant throws an exception, the Coordinator service should log the exception. The Coordination.fail(Throwable) method which is notifying this Participant must continue notification of other registered Participants.