To record my reaction to this on this
thread...
An explicit createServiceReference method
is a good idea. Given this, I think that getServiceReference should either be
removed or its sematics should be that it returns the same service references
as was (or would be) injected for the component. In other words, its result is
not affected by calls to createServiceReference().
I don’t feel strongly about whether
we remove it or keep it with those semantics. I do not, however, think that “get”
should be given the liberal semantics that Simon suggested.
Michael
From: Simon
Nash [mailto:NASH@uk.ibm.com]
Sent: Thursday, March 13, 2008
11:04 AM
To: OASIS Java
Subject: [sca-j] ISSUE 8: PROPOSED
RESOLUTION: Concurrency model for Service Reference instances
I presume the reference is to a conceptual internal
"set" that would occur as part of rewiring, and not any
"set" API call. With this understanding, your model for
"get" semantics makes sense.
In
our last discussion of the issue there seemed to be some support for
introducing a createServiceReference() method on ComponentContext to return a
newly created ServiceReference object. This solves the problem and is
upward compatible with the existing API. We would probably also need to
add a createServiceReferences() method with similar semantics.
The
question then arises whether the semantics of getServiceReference() or
getServiceReferences() need to be specified more tightly, for example to always
return the same instance that was returned by a previous call except in some
specified list of cases. I believe this isn't necessary or desirable, as
it would over-constrain implementation flexibility.
So
my proposal is to resolve this issue by adding the following methods to
ComponentContext():
<B>
ServiceReference<B> createServiceReference(Class<B> businessInterface,
String referenceName) - Returns a newly created typed service reference for a
business interface type and a reference name. This method MUST throw an
IllegalArgumentException if the reference has multiplicity greater than one.
<B>
Collection<ServiceReference<B>>
createServiceReferences(Class<B> businessInterface, String referenceName)
- Returns a list of newly created typed service references for a business
interface type and a reference name.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM
Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
"Michael
Rowley" <mrowley@bea.com>
28/02/2008 21:50
|
To
|
Simon Nash/UK/IBM@IBMGB, "OASIS
Java" <sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
|
RE: [sca-j] AW: ISSUE 8: Concurrency model
for Service Reference instances
|
|
Wiring being changed requires a call to set(), so naturally,
after a set() the value of get() will be different. If someone accesses a
reference twice, without the reference having been modified between those two
accesses, then one would expect to get the same thing.
Michael
From: Simon Nash
[mailto:NASH@uk.ibm.com]
Sent: Thursday, February 28, 2008 7:35 AM
To: OASIS Java
Subject: RE: [sca-j] AW: ISSUE 8: Concurrency model for Service
Reference instances
The semantics of getServiceReference() require it to return a newly created
object in some cases. Specifically, this will happen when wiring has been
changed, as we agreed in the resolution to issue 4. For consistency and
simplicity, I think this API should always return a newly created object. The
business logic can cache the result of a previous call if it doesn't want a new
object to be created.
Simon
Simon C. Nash, IBM Distinguished Engineer
Member of the IBM
Academy of Technology
Tel. +44-1962-815156 Fax +44-1962-818999
"Michael
Rowley" <mrowley@bea.com>
23/02/2008 16:05
|
To
|
"Barack, Ron"
<ron.barack@sap.com>, "OASIS Java"
<sca-j@lists.oasis-open.org>
|
cc
|
|
Subject
|
RE: [sca-j] AW: ISSUE 8: Concurrency model
for Service Reference instances
|
|
I agree.
I guess my concern is that the scenario in which the P2 solution is helpful is,
IMO, unlikely. One of the problems is that I view threads as being used
for only a few seconds at a time, while I expect that conversations will
typically last much longer than that.
So, what is the harm in P2? I suppose the main harm is in the name of the
routine: getServiceReference(). In my opinion, a “get”
doesn’t sound like a create, and so I would be surprised if I got a newly
created object with each call to get(). If the API to do create were
called createServiceReference() instead, I suppose I would be less
uncomfortable about it. I suppose the question would then be, how the
client get the “current” conversational service reference for the
reference, as I believe that the most common case is that there will only be one
conversation going at a time, especially when the client is conversation
scoped.
Michael
From: Barack, Ron [mailto:ron.barack@sap.com]
Sent: Friday, February 22, 2008 1:26 PM
To: Michael Rowley; OASIS
Java
Subject: AW: [sca-j] AW: ISSUE 8: Concurrency model for Service
Reference instances
Hi Michael,
So my understanding of the scenario was wrong... the component isn't a
middleman, he's a funnel. All the requests that come in, regardless of
the client conversation go into the same conversation with amazon.
In this case, the deeper problem is not the race condition of
setConversationID, but whether or not ServiceReference is thread safe, and in
particular if the service invocation must be implemented in a thread safe
manner. AFAIK, the spec currently makes no such statement. OTOH, do
we say anywhere that service reference are NOT thread safe. Should we?
I believe your code sample to be correct, and believe that it's the client's
responsibility to handle synchronization in this case. I'm not sure how
to even express the behavior that the runtime would need, in order to relieve
the client of this responsiblity.
But that wasn't my understanding of Issue-8. Issue-8 is, I thought, 2
threads, both of which want to participate in seperate conversations, using the
same service reference. Does everyone now agree that they cannot?
In a way, your solution is consistent with P2... which also does not proposal
any new functionality, but says to use the tools that already there to solve
the problem. In your scenario, where the reference is shared, the client
must perform synchronization. In my scenario, where different threads
participate in different conversations, then ComponentContext must be used as a
ServiceReference factory.
Ron
Von: Michael Rowley
[mailto:mrowley@bea.com]
Gesendet: Freitag, 22. Februar 2008 18:49
An: Barack, Ron; OASIS Java
Betreff: RE: [sca-j] AW: ISSUE 8: Concurrency model for Service
Reference instances
Ron,
Thanks for taking on my challenge. Responses inline...
From: Barack, Ron [mailto:ron.barack@sap.com]
Sent: Thursday, February 21, 2008 5:15 PM
To: OASIS Java
Subject: [sca-j] AW: ISSUE 8: Concurrency model for Service
Reference instances
Hi Michael,
Let me first make sure I understand your scenario. The composite-scoped
component is essentially a middle man, involved in 2 conversations, with amazon
on one side, and the customer on the other. And the problem is to make
sure that requests coming in from the client conversation get passed to the
correct amazon conversation. Both conversations can be long running.
Does that fit?
I think the scenario we had in mind was much more oriented to short lived
conversations. That is, the case where the whole conversation with StoreRef
takes place within a single call to buy books. In this case, you simply have to
replace the injected field "storeRef" with an injected
ComponentContext, and the implementation of buyBook would call
context.getService("storeRef"). Whether the component calls
storeRef.setConversationID or not, you never have any race conditions.
<MR>I’ve always described conversational services as being designed
to enable conversations between components that can possibly take days (as
would this book buying example).</MR>
The situation is more complex for these long-running conversations, and I think
it's unsurprising that the code would be, too. In this case, the code
would need to map from the client conversation ID, to the ID of the amazon
conversation. That is, instead of checking if storeRef.getConversation()
is null, the code would call something like
lookupStoreRefId(context.getRequestContext().getServiceReference().getConversationID()).
If the value returned was non-null, the component it would set the
storeRef.conversationID accordingly. Otherwise, it sets the conversionID
to chooseID(). The method lookupStoreRefId probably would use a DB, but
could use an in memory map, or anything else.
<MR>It doesn’t sound like that would solve the problem that my
hypothetical developer is trying to solve. He is trying to maintain a
single outstanding conversation with Amazon, in order to batch up orders of
books (possibly to qualify for free shipping?). Your solution seems to
introduce multiple simultaneous conversations, which would defeat the purpose
of this batching. Also, RequestContext...getConversationID() called from
within the BookBatch component would not return anything, since the
communication to BookBatch would probably be non-conversational – after
all, it is composite scoped.</MR>
What I don't understand is howthe alternative proposal, "P1", would
work. Are you expecting the runtime in inject storeRef's conversationID
into some thread local storage before invoking buyBooks? In this case, isn't
the implication that the runtime would be maintaining the map, just like
proposal P2 demands that the client do? Or are you assuming that the
conversationID is already on the thread from previous calls to
setConversationID? In this case, it's true that the client remains very
simple, but the solution requires
a) that the calls in the conversation always occur in the same
thread, and
b) that the server will not be restarted during the lifetime of
the conversation.
<MR>I’m not arguing for P1. I’m arguing that whatever
solution we come up with should solve the scenario that I laid forth, since it
is, I believe, the most common scenario where people will run into this
problem. And, naturally, I’d like for it to be fairly easy to use.
I suspect that the solution will include some kind of lock, from the time
that the client decides to set the ConversationID, until the business method is
called. Perhaps like this:
void buyBook(String ISBN) {
if (storeRef.getConversation() != null) {
storeRef.getService().addToCart(ISBN);
} else {
synchronized(storeRef) {
if (storeRef.getConversation()
== null) {
storeRef.setConversationID(chooseID());
}
storeRef.getService().addToCart(ISBN);
} // synchronized
}
if (isTimeToCheckOut())
checkOut();
}
In this solution, the call first call in the conversation has to be in a mutex
section with the code that sets the conversation ID, so it will not be
concurrent. However, all subsequent calls on the conversation can be
concurrent. Note that this solution is neither P1 nor P2. It
basically just says that Java synchronization needs to be used.
</MR>
Michael
Ron
Von: Michael Rowley
[mailto:mrowley@bea.com]
Gesendet: Donnerstag, 21. Februar 2008 20:45
An: OASIS Java; Barack, Ron
Betreff: ISSUE 8: Concurrency model for Service Reference instances
Here is the description of the issue 8 problem (from the PPT on today’s
call):
While the current text says that a service reference represents a single
conversation, it is not clear how a multi-threaded client should protect a
non-conversational service reference's configuration (conversation id,
callback, etc) so that it stays unmodified by other threads until an actual
invocation is executed.
Consider the following code snippet for example:
class AComponent {
@Reference ItemCheckerService srv;
void goCheckItem(long ticket, String itemId) {
ServiceReference sr = (ServiceReference) srv;
sr.setConversationID(ticket);
srv.check(itemId);
}
}
A simple synchronization may lead to strict serialization of remote calls which
is generally undesirable.
I think we should have a good idea of the likely scenarios in which this
multi-threading will happen. On today’s call, Simon suggested that
code could start its own threads. I agree this is true, but I don’t
want to concentrate on that case, since I think people who go there are willing
to be pretty sophisticated about the threading logic.
I believe other cases are that the client could be conversation or composite
scoped. Stateless and request scoped components are only active for one
thread at a time. This is implied by the semantics of the @Init and
@Destroy methods, which are called at the beginning and end of the scope
lifetime. For a stateless scope, that lifetime is one call. For
request scope, it is one remotable call (to be clarified based on one of our
open issues).
The scenario where a conversation-scoped client could be active in two threads
at once is possible, but unlikely, so I’ll concentrate on the case where
the client is composite scoped.
Consider this scenario: a composite scoped component exists for the purpose of
batching up book orders to Amazon. When orders come in to the BookBatch
component, it forwards them on to Amazon, using the shopping cart that is
associated with the current conversation. After a certain amount of time,
or a certain number of books, the current batch is purchased, and the
conversation is ended. When the next book order comes in, a new batch
(conversation) will be started. How might this look:
@Scope(“COMPOSITE”)
class BookBatch {
@Reference BookStore store;
void buyBook(String ISBN) {
store.addToCart(ISBN);
if (isTimeToCheckOut())
checkOut();
}
boolean isTimeToCheckOut() {}
void checkOut() {}
}
This seems like a potentially common scenario where the client would be
multi-threaded. Now, to run into the problem, we have to imagine that the
client wanted to choose its own conversation ID. So, perhaps it would
look like this:
@Scope(“COMPOSITE”)
class BookBatch {
@Reference ServiceReference<BookStore> storeRef;
void buyBook(String ISBN) {
if (storeRef.getConversation() == null)
storeRef.setConversationID(chooseID());
storeRef.getService().addToCart(ISBN);
if (isTimeToCheckOut())
checkOut();
}
boolean isTimeToCheckOut() {}
void checkOut() {}
String chooseID() {} // Choose a conversation ID for the next bookstore
conversation.
}
In this version, we pick a new conversation ID if a conversation isn’t
already going and set it on the service reference.
This version has a race condition! Multiple threads could have null
returned from getConversation() and so multiple threads will attempt to choose
the next conversation ID. In this particular case, it probably doesn’t
matter which one wins that race, but I suppose that in some cases it would
matter.
Is this the problem we are trying to solve? If so, I’m not sure how
the proposal in the PPT presentation given today would help much.
Ron or Simon, would you be willing to modify this class so that it works
correctly given the proposed resolution to issue 8?
Michael
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