sca-j message
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
| [List Home]
Subject: Re: [sca-j] ISSUE 25: Detailed proposal for non-conversational callbacks
- From: Simon Nash <NASH@uk.ibm.com>
- To: OASIS Java <sca-j@lists.oasis-open.org>
- Date: Mon, 11 Aug 2008 15:47:37 +0100
Jim,
I think what you are saying here is
almost exactly equivalent to the proposal that I put forward in March.
I moved away from this because of concerns from other TC members
(including yourself and Mike) that there are cases that benefit from having
the infrastructure carry per-call correlation information in a callback.
If this is a requirement, I believe my current proposal is
a simpler way to provide it than any other approach that has yet been put
forward, including the current spec.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
Jim Marino <jim.marino@gmail.com>
08/08/2008 22:22
|
To
| OASIS Java <sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
| Re: [sca-j] ISSUE 25: Detailed proposal
for non-conversational callbacks |
|
Hi Simon,
I question the utility of CallableReference and believe
having the runtime perform this type of correlation introduces more complexity
for developers than delegating to application code. So, I'd like us to
consider a simple alternative: requiring the application to flow specific
callback correlation as operation data. Of course, the runtime would still
be responsible for callback routing, i.e. dispatching to the correct client
instance. This may come as a surprise given my proposal for conversations,
but it is one case where I believe application code can handle correlation
in a more elegant and straightforward fashion.
I prefer this approach for the following reasons:
1. It is easier and more readable from the application
developer perspective (shown below)
2. This alternative explicitly acknowledges coupling of
the client and service provider via callback correlation in the service
contract. When a client and service provider are coupled via shared state,
the @Conversational annotation is used to declare that on the service contract.
When a client and service provider are coupled via a bidirectional interface,
the @Callback annotation is used to declare that on the service contract.
When a client and service provider are coupled via callback correlation,
there is nothing analogous in your proposal. My alternative would declare
that coupling as part of the service contract, in the operation signatures.
3. This alternative would add very little to the spec
and allow us to remove a bunch of extraneous APIs that are not needed.
4. It avoids the distinction in API usage for stateless
and conversational scoped components introduced in your proposal. I provide
an example of where this is the case in my comments below. I believe that
distinction will lead to confusion and is error-prone for developers.
5. It is much easier to test out-of-container.
I've in-lined alternative examples using this approach
to illustrate these points.
Jim
On Aug 7, 2008, at 3:43 PM, Simon Nash wrote:
Here is a proposed semantic model for callbacks that is independent of
implementation type or binding type.
Insert the following text after line 2455 in the SCA Assembly spec, CD01
rev1.
A call from a client component to a service that is defined using a bidirectional
interface element (a bidirectional service) conceptually carries
the following information in additional to its business data. The
actual form of the information that is passed from client to service depends
on the binding that is used for the wire connecting the reference and the
service.
The information passed is as follows:
1. A callback address that the service can use to make callback invocations
on the client. This MUST either be a physical address such as a network-addressable
endpoint to which callback invocations are made, or a logical identifier
that can be used by the client to identify a callback that it is expected
to process (for example, if the client obtains callbacks by polling). This
address MUST be present on every service invocation, and MUST be generated
by the client.
2. An invocation identifier that the client can use to correlate callback
invocations with service invocations that it has made previously. This
identifier MAY be present on a service invocation, as decided by the client.
If it is present, it MUST be generated by the client. If a
bidirectional service receives an invocation containing this identifier,
the service MUST attach the same invocation identifier to all callbacks
that are semantically associated with that service invocation, as determined
by the service's business logic. SCA runtimes MUST support client-side
comparison of invocation identifiers for equality or inequality with other
invocation identifiers.
The generation of a callback address is determined by the binding in use,
and is described in the relevant binding specification. The generation
and comparison of invocation identifiers by a client is determined by the
client's language implementation, and is described in the relevant implementation
specification. The association of callback invocations to service
invocations is determined by the service's language implementation, and
is described in the relevant implementation specification.
- - - - - - - - - - - - - - - - -
Here is a proposed Java language binding for the above semantic model.
For a Java client of a bidrectional service, the invocation identifier
is determined by the CallableReference object that was used for the invocation.
This is the CallableReference object that would be obtained by invoking
the ComponentContext.cast() API on the type-safe proxy through which the
invocation was made. If a client wishes to attach a different invocation
identifier to a subsequent invocation, it does this by calling the CallableReference.createCallableReference()
API to create a new CallableReference object, and using this CallableReference
object to make the subsequent invocation. The CallableReference object
returned by createCallableReference() MUST have an invocation identifier
that differs from the invocation identifiers of the original CallableReference
object and all other CallableReference objects created from that object
by invoking its createCallableReference() method.
No wording necessary with the alternative. We could potentially
suggest an approach if it was desired.
The following example code snippet illustrates
the simple case where the same invocation identifier is passed on every
invocation:
@Remotable
@Callback(OrderConfirm.class)
public interface FruitStore {
void orderFruit(String type, int quantity);
}
@Remotable
public interface OrderConfirm {
void confirmOrder(boolean accepted);
}
@Reference
private FruitStore store;
private int confirmed;
public void placeOrder() {
store.orderFruit("apples", 12); // passes
the invocation identifier for the CallableReference object associated with
the "store" proxy
store.orderFruit("apples", 24); // passes
the same invocation identifier
}
public void confirmOrder(int available) {
confirmed += available;
}
No changes to the above. However, the comments would not
apply.
The following example code snippet illustrates
the ability to pass a different invocation identifier on every invocation
for correlation purposes:
@Reference
private CallableReference<FruitStore> store;
private CallableReference<FruitStore> appleOrder;
private CallableReference<FruitStore> plumOrder;
private int confirmedApples;
private int confirmedPlums;
public void placeOrders() {
appleOrder = store.createCallableReference(); // creates
a new CallableReference object with its own invocation identifier
appleOrder.getService().orderFruit("apples", 12);
// passes the invocation identifier for appleOrder
appleOrder.getService().orderFruit("apples", 24);
// passes the same invocation identifier
plumOrder = store.createCallableReference(); // creates
a new CallableReference object with its own invocation identifier
plumOrder.getService().orderFruit("plums", 12);
// passes the invocation identifier for plumOrder
}
@Context
ComponentContext context;
public void confirmOrder(int available) {
CallableReference<FruitStore> ref = context.getRequestContext().getServiceReference();
A minor nit: the above line won't compile. It could be
genericized as follows to correct this:
CallableReference<FruitStore> ref = context.getRequestContext().getServiceReference(FruitStore.class);
if ref.equals(appleOrder) {
confirmedApples += available;
} else if ref.equals(plumOrder) {
confirmedPlums += available;
} else {
// should not happen
}
}
Since we are doing service-based design, I modified the
FruitStore contract to be document-centric:
@Remotable
@Callback(OrderConfirm.class)
public interface FruitStore {
void orderFruit(Order order);
}
@ XmlRootElement // assuming JAXB
public class Order implements Serializable {
public Order(String type,
int qty, String id) {
// ....
}
String getType() {
// ...
}
void setType(String type)
{
// ...
}
int getQuantity() {
// ...
}
void setQuantity(int qty) {
//...
}
String getOrderId() {
// ...
}
void setOrderId(String id)
{
// ...
}
}
@XmlRootElement // assuming JAXB
public class OrderResponse implements Serializable {
public Order(int available,
String id) {
// ....
}
int getAvailable() {
// ...
}
void setAvailable(int qty) {
//...
}
String getOrderId() {
// ...
}
void setOrderId(String id)
{
// ...
}
}
@Reference
private FruitStore store;
private int confirmedApples;
private int confirmedPlums;
public void placeOrders() {
Order applesOrder = new Order("apples",
12, "applesOrder");
store.orderFruit(applesOrder);
applesOrder = new Order("apples", 24, "applesOrder");
store.orderFruit(applesOrder);
Order plumOrder = new Order("plums", 12, "plumsOrder");
plumOrder..orderFruit(plums);
}
public void confirmOrder(OrderResponse response) {
if ("applesOrder".equals(response.getOrderId())
{
confirmedApples += available;
} else if (("plumsOrder".equals(response.getOrderId())
{
confirmedPlums += available;
} else {
// should not happen
}
}
IMO this option is much more readable and has the advantage
of working nicely over Web Services (with JAXB), JMS, RMI, or locally.
Comparing the two approaches, could you enumerate the
advantages your approach provides? It seems as if in those scenarios
the API is just getting in the way of what the developer is trying to achieve.
For a Java bidirectional service, the
invocation identifier for a callback made by that service is determined
by the CallableReference object that was used for the callback invocation.
This is the CallableReference object that would be obtained by invoking
the ComponentContext.cast() API on the type-safe proxy through which the
callback invocation was made. When the SCA runtime injects a proxy
or a CallableReference object for a callback, or returns a CallableReference
object from the RequestContext.getServiceReference() API, the CallableReference
object MUST be associated with the invocation identifier from the service
call, and the SCA runtime MUST use this invocation identifier for all callback
invocations made though any proxy for which the ComponentContext.cast()
API would return this CallableReference object.
The following example code snippet illustrates the use of an injected proxy
to obtain the callback reference. This coding style is useful for
stateless-scoped service implementations.
@Callback
OrderConfirm confirm; // captures the invocation identifier passed
on this call
public void orderFruit(String fruit, int quantity) {
confirm.confirmOrder(quantity); // passes back the
invocation identifier
}
The following example code snippet illustrates the use of the RequestContext
object to obtain the callback reference. This coding style is useful
for composite-scoped or conversation-scoped service implementations.
@Context
ComponentContext context;
public void orderFruit(String fruit, int quantity) {
CallableReference<FruitStore> ref = context.getRequestContext().getServiceReference();
// captures the invocation identifier passed on this call
This last statement also needs to be genericized as above.
OrderConfirm cbRef = ref.getCallback();
cbRef.confirmOrder(quantity); // passes back the invocation
identifier
}
The above would be rewritten as follows for both stateless
and conversation scoped components:
@Callback
protected OrderConfirm cbRef;
public void orderFruit(Order order) {
OrderResponse response = new OrderResponse(order.getQuantity(),
order.getOrderId());
cbRef.confirmOrder(response);
}
Note the above replaces the two versions of the class
with a single alternative. This avoids the distinction between stateless
and conversational scoped components your proposal introduces. I would
argue that distinction is extremely error-prone for developers. Another
advantage is it is easier to test. In unit testing, your approach would
require behavioral mock objects to mimic ComponentContext, RequestContext,
CallableReference, and OrderConfirm. My approach would need to mock out
only OrderConfirm, a user class.
- - - - - - - - - - - - - - - - -
The above proposed Java API also requires the following spec changes or
clarifications to the JavaCAA spec:
1. Object identity round-tripping and state sharing for ComponentContext.cast()
<--> CallableReference.getService(). This would become the
resolution for JAVA-10.
2. Addition of a getCallback() method to CallableReference.
3. Changing the semantics of RequestContext.getServiceReference() when
called within a callback method, so that this API returns a CallableReference
object for the service method that made the callback invocation.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
Unless stated otherwise above:
IBM United Kingdom Limited - Registered in England and Wales with number
741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
3AU
Unless stated otherwise above:
IBM United Kingdom Limited - Registered in England and Wales with number
741598.
Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
3AU
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
| [List Home]