sca-j message
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
| [List Home]
Subject: Re: [sca-j] Another early morning brainstorm - conversations revisited
- From: Simon Nash <NASH@uk.ibm.com>
- To: OASIS Java <sca-j@lists.oasis-open.org>
- Date: Thu, 24 Jul 2008 11:58:08 +0100
Jim,
Thanks for these excellent questions.
My responses are in <scn>...</scn> below.
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>
21/07/2008 17:46
|
To
| OASIS Java <sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
| Re: [sca-j] Another early morning brainstorm
- conversations revisited |
|
Hi,
I'm sorry I was unable to attend the second half of Wednesday's
meeting so want to caveat my response by saying I may be missing some of
the discussion context. I'll break down my response into several parts.
1. Question on CallableReference
I think it would be helpful if all of the code examples
were first laid out, particularly since I don't think the examples can
be made to compile (I'm probably missing something here). One of the places
I'm having trouble is with the genericized version of CallableReference,
which I assume takes the forward service interface as a parameter type.
So, if OrderService is defined as:
public interface OrderService {
public void orderApples(int quantity, CallableReference<OrderService>);
public void orderPlums(int quantity, CallableReference<OrderService>);
}
I'm having trouble seeing how this can work:
public void orderApples(int quantity,
CallableReference<OrderService> myClient) {
boolean success = placeOrder("apples", quantity);
myClient.getCallback().reportResult(success);
}
without adding a parameter type to CallableReference taking
the callback interface, as in CallableReference<FORWARD, CALLBACK>.
If this were added, I think the programming model would be very cumbersome
and would require people to declare a forward-only CallableReference as
CallableReference<SomeInterface, Void>. I may be missing something
really basic here.
<scn> I didnt't try compiling this, and when I wrote
it I did wonder whether it would work as written. You may be correct
that it would need a cast of the result of getCallback() in order to compile.
The code would then look like:
public void orderApples(int quantity,
CallableReference<OrderService> myClient) {
boolean success = placeOrder("apples", quantity);
((OrderCallback)myClient.getCallback()).reportResult(success);
}
I agree that it would be bad to add a second generic parameter
to CallableReference.</scn>
I'm also really confused by the following:
CallableReference<OrderService>
myConversation = myService.startNewOrder(); // returns an ID for the entire
fruit order
CallableReference<OrderService> myAppleOrder = myConversation.createCallbackReference();
// myAppleOrder is linked to myConversation
myAppleOrder.orderApples(12); // the infrastructure sends IDs for both
myConversation and myAppleOrder
CallableReference<OrderService> myPlumOrder = myConversation.createCallbackReference();
// myPlumOrder is linked to myConversation
myPlumOrder.orderPlums(6); // the infrastructure sends IDs for both myConversation
and myPlumOrder
Specifically:
- I don't think "myAppleOrder.orderApples(12)"
will compile
<scn> Sorry, you are correct. This should
be myAppleOrder.getService().orderApples(12);</scn>
- What is the purpose of myAppleOrder and myPlumOrder?
Where would these be used?
<scn> These are "proxy" CallableReference
objects that contain an identity for the callback. They are both
linked to the CallableReference object myConversation, which contains an
identity for the conversation. Note that they are created using the
createCallbackReference() API, not the createCallableReference() API. This
proxy-linking arrangement allows both CallableReference objects to be immutable,
while providing the logical equivalent of both a CallbackID and a ConversationID
on the forward call.</scn>
2. Clarification on service operation signatures
I'm unclear if by the following the proposal intends to
require use of CallableReference for conversational interactions:
A simple extension to the model already
proposed can solve both these problems. A conversation would be initiated
by the service creating a CallableReference and returning it to the client.
This CallableReference contains an identity for the conversation.
This client then makes multiple calls through this CallableReference
instance. Because these calls all carry the same identity, a conversation-scoped
service will dispatch all of them to the same instance.
I'm assuming this is just for illustrative purposes and
it would be possible for a conversation to be initiated in response to
the following client code, which does not use the CallableReference API:
public class OrderClient ... {
@Reference
protected OrderService service;
public void doIt() {
service.orderApples(...);
service.orderPlums(...); // routed to the same target
instance
}
}
Is this correct?
<scn>In a word, No. All conversations would
need to be initiated by the proposed mechanism of having the server return
a CallableReference to the client. This allows the conversation identity
to be generated by the server, not the client. Several people (e.g.,
Anish and Mike) have called this out as an issue with the current mechanism
for conversations.</scn>
3. Clarification on how a conversation can be ended
The proposal states:
This combination of having the service
initiate a conversation and the client decide when to end it provides the
correct combination of semantics and resolves Anish's first concern..
Would it still be possible to have the forward conversation
end as a result of a callback as in onFullfilled(..) being called on the
following interface, without the client having to do something specific
in response?
public interface OrderCallback {
@EndsConversation
void onFullfilled(..t);
}
<scn>I am not sure whether this would be possible.
The client already possesses a CallableReference object containing
a conversation identity, and it could continue to make calls through this
reference whatever calls have been made from the service provider to the
client. With my proposal, there would not be an active conversation
in the callback direction, only in the forward direction. The service
provider could do something locally to end the conversation that it has
initiated, but making the client aware of which conversation was ended
(if the client has multiple conversations in progress with the same service)
would require more mechanism than I am proposing. This is because
the @endsConversation callback cannot identify which client-side CallableReference
object was used to make the forward call to which the @endsConversation
callback is related.</scn>
4. Clarification on @Callback
Could the following example:
@Context
RequestContext requestContext;
public void orderApples(int quantity) {
boolean success = placeOrder("apples", quantity);
requestContext.getCallback().reportResult(success);
}
be re-implemented as:
@Callback
protected OrderCallback callback;
public void orderApples(int quantity) {
boolean success = placeOrder("apples", quantity);
callback.reportResult(success);
}
The re-written version (which can be done with the existing
calback mechanisms) is IMO less complex and much easier to test.
<scn>The simpler version would work for stateless
providers, or for conversational providers whose clients don't use per-call
corellation identities, but not for conversational providers whose clients
use per-call corellation identities. This is because the @Callback
was injected into a conversational instance when the instance was created
at the start of the conversation. If the orders for apples and plums
carry different client-side corellation identities (the equivalent of having
different CallbackIDs with the current APIs), then the callback proxy can't
be obtained from an injected field in the conversational instance because
it may not carry the correct client correlation identity for the actual
call being made. The same issue exists today with the current APIs,
and this was the conclusion we reached some months ago when this was discussed.</scn>
----------------
As a general comment, I consider it an anti-pattern to
put infrastructure-specific types in service contracts (e.g. CallableReference)
since it ties a priori both provider and client implementations to specific
infrastructure choices and makes testing much more difficult (APIs have
to be mocked out). I'm almost tempted to say CallableReferences should
almost never be passed around as part of service signatures (use strong
types and pass proxies instead). I'm sure that may be controversial.
<scn>Given that SCA does currently support putting
CallableReference in a business signature, I think it's reasonable to use
this mechanism to initiate conversations. I'm open to other ideas
for how to pass the conversation information from provider to client when
a conversation is started, so that the client can create a CallableReference
from that information.</scn>
Thanks,
Jim
On Jul 18, 2008, at 5:45 AM, Simon Nash wrote:
In Wednesday's discussion at the F2F, Anish made the point that using the
proposed CallableReference model to handle conversations has the problem
that it requires the client to create the "conversation ID",
but this responsibility should be with the server. Anish was also
concerned that combining the "callback ID" and "conversation
ID" concepts changes the callback programming model depending whether
or not a conversation is in progress.
A simple extension to the model already proposed can solve both these problems.
A conversation would be initiated by the service creating a CallableReference
and returning it to the client. This CallableReference contains an
identity for the conversation. This client then makes multiple calls
through this CallableReference instance. Because these calls all
carry the same identity, a conversation-scoped service will dispatch all
of them to the same instance.
This combination of having the service initiate a conversation and the
client decide when to end it provides the correct combination of semantics
and resolves Anish's first concern..
To resolve Anish's second concern, a bit more programming or an extra mechanism
is needed. If the client is using a conversational CallableReference
that was created by the service, it can't add its own correlation ID to
this CallableReference because CallableReferences are immutable. If
it needs this per-call correlation, it would need to create a new CallableReference
and pass this on the call. One way to do this would be to use business
data. The client code would look like this:
CallableReference<OrderService> myConversation = myService.startNewOrder();
// returns an ID for the entire fruit order
CallableReference<OrderService> myAppleOrder = myConversation.createCallableReference();
// create an ID for the apple order request
myConversation.orderApples(12, myAppleOrder); // the infrastructure sends
an ID for myConversation
CallableReference<OrderService> myPlumOrder = myConversation.createCallableReference();
// create an ID for the plum order request
myConversation.orderPlums(6, myPlumOrder); // the infrastructure sends
an ID for myConversation
The service interface looks like this:
public interface OrderService {
public void orderApples(int quantity, CallableReference<OrderService>);
public void orderPlums(int quantity, CallableReference<OrderService>);
}
The service provider code looks like this:
public void orderApples(int quantity, CallableReference<OrderService>
myClient) {
boolean success = placeOrder("apples", quantity);
myClient.getCallback().reportResult(success);
}
This works, but it makes the business interface rather cumbersome, and
the business interface and service provider programming model are different
from the non-conversational case. If we want to optimize the business
interface for this case further, and make the service provider programming
model the same for the conversational and non-conversational cases, we
could allow CallableReferences to be linked together so that the CallableReference
for the callback delegates its forward call identity to a second CallableReference
for the forward call. Using this approach, the client code would
look like this:
CallableReference<OrderService> myConversation = myService.startNewOrder();
// returns an ID for the entire fruit order
CallableReference<OrderService> myAppleOrder = myConversation.createCallbackReference();
// myAppleOrder is linked to myConversation
myAppleOrder.orderApples(12); // the infrastructure sends IDs for both
myConversation and myAppleOrder
CallableReference<OrderService> myPlumOrder = myConversation.createCallbackReference();
// myPlumOrder is linked to myConversation
myPlumOrder.orderPlums(6); // the infrastructure sends IDs for both myConversation
and myPlumOrder
The service interface now looks like this:
public interface OrderService {
public void orderApples(int quantity);
public void orderPlums(int quantity);
}
The service provider code is now identical to the non-conversational case
and looks like this:
@Context
RequestContext requestContext;
public void orderApples(int quantity) {
boolean success = placeOrder("apples", quantity);
requestContext.getCallback().reportResult(success);
}
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]