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, 7 Aug 2008 12:40:55 +0100
Jim,
Sorry to hear about your trip from hell.
We have all had those! My latest reponses are in <scn3>...</scn3>.
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>
07/08/2008 00:18
|
To
| OASIS Java <sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
| Re: [sca-j] Another early morning brainstorm
- conversations revisited |
|
Simon,
Sorry I missed this week's Java but I was on the transatlantic
flight-from-hell and arrived too late to attend.
Comments inline.
Jim
On Aug 4, 2008, at 12:09 PM, Simon Nash wrote:
Jim,
Simplicity for end users and common use cases is very important. However
it is not very easy to agree what is simple, or what use cases are common!
If we really want simplicity, we probably need to be willing to give
up some current functionality and delegate more to the client business
logic rather than providing sophisticated infrastructure mechanisms.
I agree it's not simple to agree on what simple is! I
also agree that simplicity may entail limiting functionality. But there
is a fine line: limit too much and the result is something very complex
for application developers at best and at worst it won't be used.
In the case of conversations and callbacks, I believe
it is possible to make things easy for the application developer by delegating
to the SCA runtime, not the application code. At the same time, based on
our implementation experience, I also believe it is relatively straightforward
to design a runtime that can handle what Mike and I have proposed.
I accept that my current proposal
doesn't do this, but tries to provide quite a lot of capability within
the infrastructure. If the TC feels this road is leading to a dead
end then we could start a very different discussion about what additional
things could be delegated to business code.
I don't think it is necessarily leading to a dead end
but it would be beneficial to step back and agree on the main use cases
we are trying to achieve as well as the extent of changes we are willing
to make. If we agree on the use cases, it will be easier to judge
the merits of the various proposals by looking at how application code
would need to be written. Use cases also have the nice effect of keeping
scope limited.
<scn3>My main concern about moving the focus of
the discussion to use cases is that we already tried to do this, and the
result was the union of everyone's opinions on what the important use cases
are (i.e., no possible use cases were eliminated.)</scn3>
That aside, I would summarize my more detailed response
below by the following points:
1. I would argue Mike's and my proposal presents a simpler
programming model to end-users. As I explain below, the issues you raised
in your response to my previous email can be resolved in a straightforward
manner.
2. I believe the requirement for a forward synchronous
call to initiate a conversation can't be made to work for common message
exchange patterns with most MOM-based bindings. Specifically, synchronous
request reply cannot be done with JMS (or proprietary messaging providers)
when using transacted messages. As I explain below, this has to do with
the way JMS messages are received on transaction commit. This goes back
to my point that the only thing we can say about conversation ids are they
are provided by the domain.
Another way of looking at this is that we may not be far
off. I do believe conversation id generation has to be synchronous but
I don't believe it should be apparent to application logic, involve a dedicated
service operation, or be required to be generated on the service provider
end. I also don't see the need for CallableReference (or ServiceReference)
but we can hold off on that topic for the time-being.
I've responded to some of your specific
comments with <scn2>.....</scn2> below.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
Hi,
I was afraid the proposal was going in this direction. This note is probably
going to sound overly negative but I'll go ahead anyway...
One of the design criteria we had with the original programming model was
simplicity for end-users, focusing on common cases, and minimal use of
APIs. I'm concerned we are beginning to loose sight of those goals. I'm
also concerned at the extent of changes being proposed and impact they
will have on the other SCA specifications.
I wonder if part of the problem is everyone has a specific set of use-cases
in mind and we are jumping into API design before fully understanding the
scenarios each of us wants to enable. Would it be beneficial to step back
and enumerate from an end-user perspective what scenarios we want to enable
first? Once we have a common understanding of these, we can look at alternative
proposals by comparing how application code would need to be written. A
preference would be to simplify what is currently in the specification
through subtraction and accommodate common use cases through existing mechanisms
as opposed to introducing a new programming model, which is being done
with the proposal. For uncommon cases, I think the bar should be "possible
but potentially difficult". This approach would hopefully bring forth
issues in design as well as usability while at the same time avoiding the
risk of introducing wholesale changes this late in the specification process.
Is this a reasonable approach?
That said, I wanted to comment more specifically on the proposal. I'll
start by summarizing my concerns and then respond specifically inline.
Jim
My general concerns are centered on:
1. The complexity introduced in this approach
The proposed API is considerably more complex then what we currently have,
which I believe can be made to work with (hopefully) no or minor changes.
It is also much more complex than alternative conversational programming
models such as Seam and the alternative simplification proposed by Mike
and myself. I'll list specific areas below where I think it is too complex.
2. The association of service contract signatures with infrastructure-specific
types
Service contracts should not have SCA- or other infrastructure-specific
types in their operation signatures. Requiring SCA types in service contract
signatures:
a. Ties clients and service provider implementations forever to SCA
(annotations are OK since they are not "required" to run - e.g.
in Java they need not be on the classpath for a class to be loaded).
b. Makes interop much more difficult to achive. For example, I'm
not sure how CallableReference would be represented to a .NET, JMS, or
JAX-WS client.
c. Is inconsistent with both service-based design (operations take
*application/business* data as parameters) and the direction most Java-based
programming models have been evolving to. That is, supporting basic Java
classes that in most scenarios have little to no code-level dependencies
on infrastructure, which promotes simplicity, a level of portability, and
makes migration to future specification versions much easier.
3. Bleeding of protocol-specific requirements into the programming model
The proposed API unnecessarily reflects a scheme where conversation ids
are generated through a forward synchronous invocation. This is not always
a requirement, and when it is the infrastructure can handle such cases
transparently to application code.
4. The apparent loss of import functionality such as what is possible today
with @EndsConversation and @Callback today (see comments inline)
5. Increased difficulty in testing
Requiring the use of CallableReference makes out-of-container testing much
more difficult than using mocks.
6. The amount of change introduced by the proposal, particularly as it
will significantly impact assembly and bindings
I believe the current proposal will require wholesale changes in the way
conversations are defined at the assembly level as well as specific binding
manifestations. For example, the current JMS binding section on conversations
would need to be completely re-written.
--------------------------------------------
More specific comments inline...
On Jul 24, 2008, at 3:58 AM, Simon Nash wrote:
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
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>
Forcing application code to cast like this is equally problematic. One
of the original proposals for programming model APIs required this and
was rejected for the following reasons:
1. It's not a discoverable API. In other words, there is nothing obvious
in the API that indicates a cast is possible and safe. This forces code
to make potentially dangerous assumptions about references. It also requires
people to "read the manual" as opposed to looking at an API or
getting "drop-down" help in an IDE.
2. From an infrastructure provider perspective it is also problematic:
it forces references to always be proxied or have a mixin applied statically
or during class load time. This prohibits optimizations where references
are directly injected.
We discussed the casting approach at length and instead chose an explicit
cast operation in the API as a cleaner alternative.
<scn2>OK, I agree that this has problems. We can drop it from
consideration and focus the discussion on the second alternative from my
original note.</scn2>
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>
What happens if I want to introduce asynchrony (non-blocking operations)
such as:
public interface OrderService {
@OneWay
void startNewOrder();
@OneWay
void orderApples(..);
@OneWay
void orderPlums(..);
}
Does the proposal require interfaces to have a synchronous operation to
start a conversation?
<scn2>Yes it does, and I believe this restriction is needed whether
or not we make API changes. The first operation of a conversation
will create an instance and initialize it for use by subsequent calls within
the same conversation. If the first operation is oneway, the client
can proceed immediately and it might make the second call before the first
call has completed (or even started) executing.</scn2>
I don't believe the current API requires this at all and
that behavior is the opposite of what I am proposing. The reference proxy
can simply make an out-of-band synchronous call to allocate a conversational
id. This may or may not involve out-of-process work. Either way, the proxy
does not return control to the client until an id is generated. The two
key things I am proposing are:
1. How the conversation id is generated does not bleed
through to the programming model
2. Id generation does not require an operation on the
target service to be invoked
Expanding on point 2, requiring a conversation to be initiated
by a synchronous operation *to the service* cannot work over JMS when the
client is using transacted messaging since messages are not sent until
after the transaction has committed This is a very common messaging scenario.
Assuming the callback is handled via a reply-to queue that the client listens
on, the consumer only receives enqueued messages when a transaction commits,
thereby inhibiting the client from receiving a response. If in the
original above example the client is participating in a global transaction
and OrderService.startNewOrder() returns a CallableReference or a proxy,
the client will hang as the forward message will not be received until
the transaction commits (which won't occur since the client would be listening
on the reply-to queue).
To avoid this, I imagine most JMS binding implementations
would use the mechanisms as described by the JMS binding spec and pass
a conversation id in the message header.
Therefore, I believe your proposal won't work in these
scenarios.
<scn3>I understand your point about JMS. However,
you haven't addressed my other point about the conversational provider
instance needing to be created and initialized before further invocations
are made on it. For transports that provide reliable queued in-order
delivery of messages (the JMS case) the transport can take care of this.
For other transports, the first invocation must execute and complete
before the second one can occur. This serialization needs to be handled
somehow, either by the application or by the infrastructure.</scn3>
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>
Sorry for being so thick, but I don't see why the above could not be supported
using "server" generation of conversation ids. We should be careful
here to specify what we mean by "server", and whether invocations
are flowing through a wire or a client external to the domain. I don't
think the term "server" should necessarily mean "the runtime
hosting the service provider." Sometimes this may be the (degenerate)
case, but not always.
For communications flowing through wires, the only thing we can likely
say is "conversation ids are generated by the domain". I
would imagine most domain implementations would chose an efficient id generation
scheme that can be done without context switching on every invocation (e.g.
UUID generation provided by the JDK). However, in cases where this efficient
identity generation is not possible, I believe SCA infrastructure can support
the above code. In this case, the reference proxies would be responsible
for making some type of out-of-band request to generate a conversation
id to some piece of domain "infrastructure".
<scn2>I'm very uncomfortable with placing this kind of runtime requirement
on the SCA domain, which IMO should not take on the responsibilities of
a persistence container. For efficient support of persistent conversational
instances with failover, load balancing and transactionality, the ID may
need to be generated by a persistence container. For example, it
could be a database key.</scn2>
I think this is less of a requirement than what you are
proposing, which also shifts the burden to application code (which should
not have to deal with these mundane infrastructure concerns).
The only requirement I am making is the "domain"
provides the key. My usage of the term "domain" is intentionally
vague: it could be a database, some service hosted in a cluster, or a snippet
of code embedded in a Java proxy. Generating the id can therefore
be done using a database key or, more simply, by having a reference proxy
use facilities already provided in the JDK 1.5 or greater, which would
require one line of code. My proposal would not restrict the SCA infrastructure
in how the id is generated, other than it is done synchronously and out-of-band.
<scn3>I'm concerned about putting too much mechanism
into the SCA domain. I think it needs to support SCA wiring, deployment
and configuration. I'd expect it other middleware that's not part
of the SCA domain to provide things like persistence, load balancing and
failover.</scn3>
The important thing is how conversation id generation
happens does not bleed into the programming model and is transparent to
the application. In other words, we not should require anything more complex
than this when the OrderClient is wired to an OrderService:
public class OrderClient ... {
@Reference
protected OrderService service;
public void doIt() {
service.orderApples(...);
service.orderPlums(...); // routed to the same target instance
}
}
<scn2>Another important thing is how much complexity is needed inside
the infrastructure to support the programming model. This cannot
always be hidden under the covers, especially when dealing with failure
cases and complex environments. The need for persistent transactional
storage of conversational instances is an example of where this complexity
arises.</scn2>
Here it would be useful to outline a specific use case
for "persistent transactional storage of conversational instances"
as I'm not sure what that entails. Does it mean the following
1. Invocations to orderApples() and orderPlums() are done
with guaranteed delivery
2. Changes to the *state* of a given OrderService instance
are guaranteed to be available in the case where the runtime hosting the
instance fails
I suspect if it is the above, much of the complexity will
be buried in the messaging and failover infrastructure, not the SCA runtime.
<scn3>SCA would need to decide whether or not such
guarantees are part of the SCA conversational programming model. If
they are, then the SCA domain would have to step up to providing the mechanisms
to implement them. It's currently unclear from the SCA specs what
assumptions, if any, application code can make about these matters.</scn3>
I will also note that other conversational programming models provided
by JBoss Seam, the JAX-WS RI, and even BEA Workshop are this easy. SCA
should not be any more complex than those alternatives.
<scn2>Do these handle the complex cases that I mentioned above, or
just deal with simple in-memory instance dispatching?</scn2>
Yes, they handle the complex cases. BEA Workshop has many
of these features. When the JAX-WS RI is run on reliable infrastructure
(e.g. WebLogic Server 10.3), it also supports these scenarios. I'm fairly
sure Seam does so using EJB3 stateful session beans although I would need
to verify that.
For communications originating from a client external to the domain, I
think the following are going to vary:
1. Whether the id is generated by the client or the service provider host
2. The "shape" of the id and how it is represented to clients.
For example, in JAX-WS clients, I believe the id would need to be encapsulated
in a javax.xml.ws.wsaddressing.W3CEndpointReference.
I think it would be a useful exercise to understand what the proposal would
require when using a JAX-WS, JMS, or .NET Web Services client. For example,
what would a JMS client be required to do to invoke a conversational service?
Specifically, how would it obtain a handle to the conversational service?
The same issues could be examined in the context of .NET or JAX-WS.
<scn2>I agree that we need to think about what can be supported here.
This adds a further dimension of complexity.</scn2>
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>
The example would actually end the conversation in the forward direction.
This is a pretty common use case (a callback ending the forward conversation
between a client and a provider) that I believe needs to be enabled. In
addition to using @EndsConversation, a provider can end the current conversation
via the RequestContext API. If both of those are not allowed in the proposal,
it would mean the only way to end a conversation is for the client to do
so or for the conversation to time out, assuming @ConversationAttributes
was used. If I understand proposed API correctly, I don't think this would
be a good thing.
<scn2>Sorry for misunderstanding your question. I think it
should be possible for the provider to end a conversation in the forward
direction via the RequestContext API. I need to think some more about
whether there are any issues with doing this as part of a callback. Off
the top of my head, it seems like this could be done on the server side,
but the client would not be notified automatically. If it tried to
make another call on the ended conversation, it would receive an exception.</scn2>
Sorry, I may not have been clear in my original question.
As to your comment about the client not being notified, In the case I outlined
above, it would be by having onFulfilled() invoked.
<scn3>OK, I understand now.</scn3>
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>
Sorry I'm not following this and it may be because I lack some context
or am making incorrect assumptions. A conversation is defined as shared
context between a client and a provider. Only one client has visibility
to that shared context and hence the correct callback correlation will
always be known. If the shared context is implemented in a runtime as routing
to the same instance, then injection of the callback proxy can safely be
done when the instance is created and cached for the lifetime of the instance.
For composite-scoped components, a smart callback proxy can be used to
always dispatch to the correct client, although that would be beyond what
the spec allows (which we did discuss several months back).
<scn2>The problem arises when different client calls have different
callback IDs, or whatever these turn into as part of callback simplification.
The injected callback proxy can only hold a single callback ID.</scn2>
I thought we had decided to remove getCallbackId()? Is
this going to be re-opened?
<scn3>Removing this has been proposed, but it has
never been agreed by the TC. Mike Edwards has argued that it is necessary
to allow different individual forward calls to carry a per-call identity
that is available to the callback business logic. My recent "hallucination"
proposal for non-conversational callbacks includes this capability.</scn3>
----------------
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>
How about the current API:
public class OrderClient ... {
@Context
protected ComponentContext context;
public void doIt() {
service.orderApples(...);
CallableReference<OrderService>
reference = context.cast(service);
//...
}
}
Or, we could change "cast(..)" to
public interface ComponentContext {
<T> CallableReference<T> getCallableReference(T proxy);
}
I believe this to be similar in spirit to working with message-based correlation
ids (e.g. JMS) where the forward message id is not available until after
the message has been enqueued.
<scn2>This doesn't work because a callback can occur before control
is returned back to the invoking thread of execution. So the client's
conversation correlator must be known before invoking any call that may
trigger a callback, in case the callback business code needs to do anything
that needs to use the conversational state. There's a similar issue
with "callback ID" (if we retain this concept), as a forward
call can invoke a callback which may need to use a previously generated
callback ID to identify the context in which the callback business code
should execute.</scn2>
This assumes the conversation id is generated asynchronously
from the client. That is not what I am proposing. The first invocation
on a proxy would not return control to the client until after a conversation
id was generated. The difference with what I am saying and what you are
proposing is that the synchronous act of generating the id:
1. Is not exposed to application code and does not place
requirements on the service contract
2. Does not necessarily require any out-of-process work
<scn3>The problem with this is that as soon as the
invocation is made on the service, a callback could arrive, and this could
happen before the client code that calls the proxy has received control
back from the SCA runtime and been able to process the generated ID.</scn3>
The above approach would avoid polluting client code which does not
need to make use of CallableReference and, more importantly, the signature
of OrderService which would not be forced to carry SCA types. If we require
services to use CallableReference, we will be forcing all providers and
all clients to always use SCA infrastructure. If in the future we
need to change the CallableReference API in some incompatible way (EJB
made significant changes three times), we run the risk of causing migration
havoc for end users given service contracts will need to be changed. This
will also make interoperability with other middleware problematic. For
example, how would an interface signature with CallableReference be represented
to a .NET or JMS client?
<scn2>How would you feel about changing the signature of startNewOrder()
so that it does not return a CallableReference but returns a typed proxy
instead? This would also require some adjustment to the client-side
API, perhaps something like:
@Context
protected ComponentContext context;
OrderService myConversation = myService.startNewOrder(); // returns an
ID for the entire fruit order
CallableReference<OrderService> myAppleOrder = context.createCallableReference(myConversation);
// myAppleOrder is linked to myConversation
myAppleOrder.getService().orderApples(12); // the infrastructure sends
IDs for both myConversation and myAppleOrder
CallableReference<OrderService> myPlumOrder = context.createCallableReference(myConversation);
// myPlumOrder is linked to myConversation
myPlumOrder.getService().orderPlums(6); // the infrastructure sends IDs
for both myConversation and myPlumOrder
Does this help at all? It eliminates the use of CallableReference
in business service APIs, though not in client business logic.</scn2>
It's a start but doesn't address my main concerns:
1. I don't think it will work for important messaging
use cases
2. It places unnecessary restrictions on conversational
service contracts, namely a forward synchronous invocation
3. It will result in a lot of unnecessary application
boilerplate code. For example, all conversational calls will need to start
with a synchronous call to the provider, even if no application or business
function is modeled.
4. Application logic is still unnecessarily tied to infrastructure
5. It's a lot more complex than the examples I gave or
alternatives to SCA
6. It's confusing, particularly this line:
OrderService myConversation = myService.startNewOrder();
// returns an ID for the entire fruit order
For example, if I am an app developer, I will ask why
do myService and myConversation implement the same interface? What's the
difference (I know what it is but one can't tell by looking at the code)?
7. Requiring an extra invocation to start a conversation
does not promote coarse-granularity. It will result in an unnecessary and
potentially costly performance impact as an additional remote call is introduced.
<scn3>I'll observe that most or all of these focus
around the proposed additional forward synchronous call that returns a
conversation ID. If we could find a way to avoid the need for this,
we might be close to agreement.</scn3>
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
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]