|
ref_send API 1.16 defensive programming in Java |
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
java.lang.Objectorg.joe_e.Struct
org.ref_send.promise.eventual.Eventual
public final class Eventual
The eventual operator.
This class decorates an event loop with methods implementing the core eventual control flow statements needed for defensive programming. The primary aim of these new control flow statements is preventing plan interference.
The implementation of a public method can be thought of as a plan in which an object makes a series of state changes based on a list of invocation arguments and the object's own current state. As part of executing this plan, the object may need to notify other objects of the changes in progress. These other objects may have their own plans to execute, based on this notification. Plan interference occurs when execution of these other plans interferes with execution of the original plan.
Interleaving plan execution is vulnerable to many kinds of interference. Each kind of interference is explained below, using the following example code:
public final class
Account {
private int balance;
private final ArrayList<Observer> observers;
Account(final int initial) {
balance = initial;
observers = new ArrayList<Observer>();
}
public void
observe(final Observer observer) throws NullPointerException {
if (null == observer) {
throw new NullPointerException();
}
observers.add(observer);
}
public int
getBalance() { return balance; }
public void
setBalance(final int newBalance) {
balance = newBalance;
for (final Observer observer : observers) {
observer.currentBalance(newBalance);
}
}
}
A method can terminate execution of its plan by throwing an exception.
The plan may be terminated because it would violate one of the object's
invariants or because the request is malformed. Unfortunately, throwing an
exception may terminate not just the current plan, but also any other
currently executing plans. For example, if one of the observers throws a
RuntimeException from its currentBalance()
implementation, the remaining observers are not notified of the new account
balance. These other observers may then continue operating using stale data
about the account balance.
When a method implementation invokes a method on another object, it
temporarily suspends progress on its own plan to let the called method
execute its plan. When the called method returns, the calling method
resumes its own plan where it left off. Unfortunately, the called method
may have changed the application state in such a way that resuming the
original plan no longer makes sense. For example, if one of the observers
invokes setBalance() in its currentBalance()
implementation, the remaining observers will first be notified of the
balance after the update, and then be notified of the balance before the
update. Again, these other observers may then continue operating using
stale data about the account balance.
A called method may also initiate an unanticipated state transition in
the calling object, while the current transition is still incomplete. For
example, in the default state, an Account is always ready to
accept a new observer; however, this constraint is temporarily not met when
the observer list is being iterated over. An observer could catch the
Account in this transitional state by invoking
observe() in its currentBalance() implementation.
As a result, a ConcurrentModificationException will be
thrown when iteration over the observer list resumes. Again, this exception
prevents notification of the remaining observers.
The above plan interference problems are only possible because execution of one plan is interleaved with execution of another. Interleaving plan execution can be prevented by scheduling other plans for future execution, instead of allowing them to preempt execution of the current plan. This class provides control flow statements for scheduling future execution and receiving its results.
Since the control flow statements defined by this class schedule future
execution, instead of immediate execution, they behave differently from the
native control flow constructs in the Java language. To make the difference
between eventual and immediate execution readily recognized by programmers
when scanning code, some naming conventions are proposed. By convention, an
instance of Eventual is held in a variable named "_".
Additional ways of marking eventual operations with the '_'
character are specified in the documentation for the methods defined by
this class. All of these conventions make eventual control flow
statements distinguishable by the character sequence "_.".
Example uses are also shown in the method documentation for this class. The
'_' character should only be used to identify eventual
operations so that a programmer can readily identify operations that are
expected to be eventual by looking for the _.
pseudo-operator.
| Field Summary | |
|---|---|
Loop<Task> |
enqueue
Schedules a task for execution in a future turn. |
Log |
log
debugging output |
| Constructor Summary | |
|---|---|
Eventual(Token deferred,
Loop<Task> enqueue,
Log log)
Constructs an instance. |
|
| Method Summary | ||
|---|---|---|
|
_(T reference)
Ensures a reference is an eventual reference. |
|
|
_(T x)
Causes a compile error for code that attempts to create an eventual reference of a concrete type. |
|
|
cast(java.lang.Class<R> type,
Volatile<?> promise)
Causes a compile error for code that attempts to create an eventual reference of a concrete type. |
|
|
cast(java.lang.Class type,
Volatile<T> promise)
Creates an eventual reference. |
|
static
|
compose(Do<A,B> first,
Resolver<B> second)
Constructs a call return block. |
|
|
defer()
Creates a promise in the deferred state. |
|
static
|
near(T reference)
Gets the corresponding immediate reference. |
|
static
|
near(Volatile<T> promise)
Gets the corresponding immediate reference. |
|
static
|
promised(T reference)
Gets the corresponding promise. |
|
|
when(T reference,
Do<T,R> observer)
Registers an observer on an eventual reference. |
|
|
when(T reference,
Do<T,R> observer)
Causes a compile error for code that attempts to return a concrete type from a when block. |
|
|
when(Volatile<T> promise,
Do<T,R> observer)
Registers an observer on a promise. |
|
|
when(Volatile<T> promise,
Do<T,R> observer)
Causes a compile error for code that attempts to return a concrete type from a when block. |
|
| Methods inherited from class org.joe_e.Struct |
|---|
equals, hashCode |
| Methods inherited from class java.lang.Object |
|---|
getClass, notify, notifyAll, toString, wait, wait, wait |
| Field Detail |
|---|
public final Loop<Task> enqueue
The implementation preserves the First In First Out ordering of tasks, meaning the tasks will be executed in the same order as they were enqueued.
public final Log log
| Constructor Detail |
|---|
public Eventual(Token deferred,
Loop<Task> enqueue,
Log log)
deferred - Deferred permissionenqueue - enqueuelog - log| Method Detail |
|---|
public <T,R> R when(Volatile<T> promise,
Do<T,R> observer)
The observer will be notified of the promise's
state at most once, in a future event loop turn. If
there is no referent, the observer's
reject method will be called with the reason;
otherwise, the fulfill method will be called with
either an immediate reference for a local referent, or an
eventual reference for a remote referent. For example:
import static org.ref_send.promise.Resolved.ref;
…
final Promise<Account> mine = …
final Promise<Integer> balance =
_.when(mine, new Do<Account,Promise<Integer>>() {
public Promise<Integer>
fulfill(final Account x) { return ref(x.getBalance()); }
});
A null promise argument is treated like an
instance of Rejected with a reason of
NullPointerException.
Multiple observers registered on the same promise will be notified in the same order as they were registered.
This method will not throw an Exception. Neither the
promise, nor the observer, argument will be
given the opportunity to execute in the current event loop turn.
T - referent typeR - observer's return type, MUST be Void, an
allowed proxy type, or
assignable from Promisepromise - observed promiseobserver - observer, MUST NOT be null
observer's return, or null if the
observer's return type is Void
java.lang.Error - invalid observer argument
public static <A,B> Do<A,java.lang.Void> compose(Do<A,B> first,
Resolver<B> second)
first - code block to executesecond - code block's return resolverpublic <T> Channel<T> defer()
The return from this method is a ( promise,
resolver ) pair. The promise is initially in the
deferred state and can only be resolved by the resolver once. If the
promise is fulfilled, the promise will
forever refer to the provided referent. If the promise, is
rejected, the promise will forever be in the
rejected state, with the provided reason. If the promise is
resolved, the promise will forever be in
the same state as the provided promise. After this initial state
transition, all subsequent invocations of either
fulfill, reject or
resolve are silently ignored. Any
observer registered on the promise
will only be notified after the promise is resolved.
T - referent type
public <T> T cast(java.lang.Class type,
Volatile<T> promise)
An eventual reference queues invocations, instead of processing them immediately. Each queued invocation will be processed, in order, in a future event loop turn.
For example,
// Register an observer now, even though we don't know what we plan // to do with the notifications. final Channel<Observer> x = _.defer(); account.observe(_.cast(Observer.class, x.promise)); … // A log output has been determined, so fulfill the observer promise. final Observer log = … x.resolver.fulfill(log); // Logs all past, and future, notifications.
If this method returns successfully, the returned eventual reference
will not throw an Exception on invocation of any of the methods
defined by its type, provided the invoked method's return type is either
void, an allowed proxy
type or assignable from Promise. Invocations on the eventual
reference will not give the promise, nor any of the
invocation arguments, an opportunity to execute in the current event loop
turn.
Invocations of methods defined by Object are not
queued, and so can cause plan interference, or throw an exception.
This method will not throw an Exception. The promise
argument will not be given the opportunity to execute in the current
event loop turn.
T - referent type to implementtype - referent type to implement, MUST be an
allowed proxy typepromise - promise for the referent
public <T> T _(T reference)
Use this method to vet received arguments. For example:
import static org.joe_e.ConstArray.array;
public final class
Account {
private final Eventual _;
private int balance;
private ConstArray<Observer> observer_s;
public
Account(final Eventual _, final int initial) {
this._ = _;
balance = initial;
observer_s = array();
}
public void
observe(final Observer observer) throws NullPointerException {
// Vet the received arguments.
final Observer observer_ = _._(observer);
// Use the vetted arguments.
observer_s = observer_s.with(observer_);
}
public int
getBalance() { return balance; }
public void
setBalance(final int newBalance) {
balance = newBalance;
for (final Observer observer_ : observer_s) {
// Schedule future execution of notification.
observer_.currentBalance(newBalance);
}
}
}
By convention, the return from this method, as well as from
cast, is held in a variable whose name is suffixed with
an '_' character. The main part of the variable name
should use Java's camelCaseConvention. A list of eventual references is
suffixed with "_s". This naming convention creates the
appearance of a new operator in the Java language, the eventual
operator: "_.".
This method will not throw an Exception. The
reference argument will not be given the opportunity to
execute in the current event loop turn.
T - referent type, MUST be an
allowed proxy typereference - immediate or eventual reference,
MUST be non-null
java.lang.Error - null reference or
T not an allowed proxy type
public <T,R> R when(T reference,
Do<T,R> observer)
The implementation behavior is the same as that documented for the
promise based when statement.
T - referent typeR - observer's return typereference - observed referenceobserver - observer, MUST NOT be null
observer's return, or null if the
observer's return type is Void
public static <T> T near(T reference)
throws java.lang.ClassCastException
This method is the inverse of _; it gets the
corresponding immediate reference for a given eventual reference.
T - referent typereference - possibly eventual reference for a local referent
java.lang.ClassCastException - no corresponding immediate reference
public static <T> T near(Volatile<T> promise)
throws java.lang.ClassCastException
The implementation behavior is the same as that documented for the
reference based near guard.
T - referent typepromise - promise for a local referent
java.lang.ClassCastException - no corresponding immediate referencepublic static <T> Volatile<T> promised(T reference)
This method is the inverse of cast; it gets the
corresponding promise for a given reference.
This method will not throw an Exception. The
reference argument will not be given the opportunity to
execute.
T - referent typereference - immediate or eventual reference
public <T extends java.io.Serializable> void _(T x)
throws java.lang.Exception
If you encounter a compile error because your code is linking to this method, insert an explicit cast to the allowed proxy type. For example,
_._(this).run();becomes:
_._((Runnable)this).run();
x - ignored
java.lang.Error - always thrown
java.lang.Exception
public <R extends java.io.Serializable> void cast(java.lang.Class<R> type,
Volatile<?> promise)
throws java.lang.Exception
If you encounter a compile error because your code is linking to this method, replace the specified concrete type with an allowed proxy type. For example,
final Logger o_ = _.cast(Logger.class, op);becomes:
final Observer o_ = _.cast(Observer.class, op);
R - referent type to implementtype - ignoredpromise - ignored
java.lang.Error - always thrown
java.lang.Exception
public <T,R extends java.io.Serializable> void when(Volatile<T> promise,
Do<T,R> observer)
throws java.lang.Exception
If you encounter a compile error because your code is linking to this method, change your when block return type to a promise. For example,
final Promise<Account> pa = …
final Integer balance = _.when(pa, new Do<Account,Integer>() {
public Integer
fulfill(final Account a) { return a.getBalance(); }
});
becomes:
final Promise<Account> pa = …
final Promise<Integer> balance =
_.when(pa, new Do<Account,Promise<Integer>>() {
public Promise<Integer>
fulfill(final Account a) { return ref(a.getBalance()); }
});
promise - ignoredobserver - ignored
java.lang.Error - always thrown
java.lang.Exception
public <T,R extends java.io.Serializable> void when(T reference,
Do<T,R> observer)
throws java.lang.Exception
If you encounter a compile error because your code is linking to this method, change your when block return type to a promise. For example,
final Account a = …
final Observer o_ = …
final Integer initial = _.when(o_, new Do<Observer,Integer>() {
public Integer
fulfill(final Observer o) { return a.getBalance(); }
});
becomes:
final Account a = …
final Observer o_ = …
final Promise<Integer> initial =
_.when(o_, new Do<Observer,Promise<Integer>>() {
public Promise<Integer>
fulfill(final Observer o) { return ref(a.getBalance()); }
});
reference - ignoredobserver - ignored
java.lang.Error - always thrown
java.lang.Exception
|
ref_send API 1.16 defensive programming in Java |
|||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
Copyright 1998-2007 Waterken Inc. under the terms of the MIT X license.