[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]
Subject: ISSUE 25: Revised proposal for non-conversational callbacks
The following is an attempt to bring forward a compromise proposal that combines my perspective and Jim's perspective on how non-conversational callbacks should be handled. My previous proposal is in http://lists.oasis-open.org/archives/sca-j/200808/msg00022.html and Jim's responses are in http://lists.oasis-open.org/archives/sca-j/200808/msg00039.html http://lists.oasis-open.org/archives/sca-j/200808/msg00042.html My previous proposal contained a client-side mechanism for correlating service invocations with their associated callbacks. Jim responded with a counter-proposal that made the correlation explicit as part of the bidirectional interface contract. Jim and I have discussed this and we believe both cases are valid and should be supported by SCA. In more detail, the cases are as follows: 1. The service contract, as defined by the service provider, includes explicit request-level correlation information in business data on the callback interface. If the client needs to perform request-level correlation, it uses the business-level correlation information that the provider has passed on the callback. If the client does not need to perform request-level correlation, it ignores the business-level correlation information passed on the callback. 2. The service contract, as defined by the service provider, does not include explicit request-level correlation information in business data on the callback interface. If the client needs to perform request-level correlation, it uses client-side SCA infrastructure mechanisms to obtain request-level correlation information when callbacks are made. These mechanisms are transparent to the service provider, as long as it follows the recommended SCA programming model for callbacks. If the client does not need to perform request-level correlation, there is nothing for either the client or provider to do. These approaches are illustrated by the following code samples. In both cases, the client performs request-level correlation on the callback. ====== CASE 1 ====== @Remotable @Callback(OrderConfirm.class) public interface FruitStore { void orderFruit(Order order); } @Remotable public interface OrderConfirm { void confirmOrder(OrderResponse response); } @XmlRootElement // assuming JAXB public class Order implements Serializable { public Order() { // ... } 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 OrderResponse() { // ... } public OrderResponse(int available, String id) { // ... } int getAvailable() { // ... } void setAvailable(int qty) { // ... } String getOrderId() { // ... } void setOrderId(String id) { // ... } } The client could be implemented as follows: public class MyClient implements OrderConfirm { @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 plumsOrder = new Order("plums", 12, "plumsOrder"); store.orderFruit(plumsOrder); } public void confirmOrder(OrderResponse response) { if ("applesOrder".equals(response.getOrderId())) { confirmedApples += response.getAvailable(); } else if ("plumsOrder".equals(response.getOrderId())) { confirmedPlums += response.getAvailable(); } else { // should not happen } } } The service provider could be implemented as follows if the implementation scope is either STATELESS or CONVERSATION. @Scope("STATELESS") // could use same code for CONVERSATION scope public class MyService implements FruitStore { @Callback protected OrderConfirm cbRef; public void orderFruit(Order order) { // ...check stock and record the order OrderResponse response = new OrderResponse(order.getQuantity(), order.getOrderId()); cbRef.confirmOrder(response); } } If the service provider implementation has COMPOSITE scope, code like the following would be needed, as callbacks are not injected into composite-scoped components. This code could also be used for STATELESS and CONVERSATION scoped implmentations. @Scope("COMPOSITE") // could use same code for STATELESS and CONVERSATION scope public class MyServiceComposite implements FruitStore { @Context RequestContext context; public void orderFruit(Order order) { // ...check stock and record the order OrderResponse response = new OrderResponse(order.getQuantity(), order.getOrderId()); OrderConfirm cbRef = (OrderConfirm)context.getCallback(); cbRef.confirmOrder(response); } } ====== CASE 2 ====== @Remotable @Callback(OrderConfirm.class) public interface FruitStore { void orderFruit(Order order); } @Remotable public interface OrderConfirm { void confirmOrder(OrderResponse response); } @XmlRootElement // assuming JAXB public class Order implements Serializable { public Order() { // ... } public Order(String type, int qty) { // ... } String getType() { // ... } void setType(String type) { // ... } int getQuantity() { // ... } void setQuantity(int qty) { // ... } } @XmlRootElement // assuming JAXB public class OrderResponse implements Serializable { public OrderResponse() { // ... } public OrderResponse(int available) { // ... } int getAvailable() { // ... } void setAvailable(int qty) { // ... } } The client could be implemented as follows. As an exercise to the reader, what is the scope of the client implementation? I'll leave you to consider this for a while, and return to this topic below. public class MyClient implements OrderConfirm { @Context RequestContext context; @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(); Order myOrder = new Order("apples", 12); appleOrder.getService().orderFruit(myOrder); myOrder = new Order("apples", 24); appleOrder.getService().orderFruit(myOrder); plumOrder = store.createCallableReference(); myOrder = new Order("plums", 12); plumOrder.getService().orderFruit(myOrder); } public void confirmOrder(OrderResponse response) { CallableReference<FruitStore> ref = context.getServiceReference(); if ref.equals(appleOrder) { confirmedApples += response.getAvailable(); } else if ref.equals(plumOrder) { confirmedPlums += response.getAvailable(); } else { // should not happen } } } The service provider could be implemented as follows if the implementation scope is either STATELESS or CONVERSATION. @Scope("STATELESS") // could use same code for CONVERSATION scope public class MyService implements FruitStore { @Callback protected OrderConfirm cbRef; public void orderFruit(Order order) { // ...check stock and record the order OrderResponse response = new OrderResponse(order.getQuantity()); cbRef.confirmOrder(response); } } If the service provider implementation has COMPOSITE scope, code like the following would be needed, as callbacks are not injected into composite-scoped components. This code could also be used for STATELESS and CONVERSATION scoped implmentations. @Scope("COMPOSITE") // could use same code for STATELESS and CONVERSATION scope public class MyServiceComposite implements FruitStore { @Context RequestContext context; public void orderFruit(Order order) { // ...check stock and record the order OrderResponse response = new OrderResponse(order.getQuantity()); OrderConfirm cbRef = (OrderConfirm)context.getCallback(); cbRef.confirmOrder(response); } } Now to return to our earlier unanswered question, what is the scope of the client implementation? It can't be STATELESS, because the callback would get a new instance with a new set of instance variables and the correlation wouldn't work. It can't be COMPOSITE, because this requires a single instance of the client component. Typically there could be many clients active simultaneously, each placing a separate order. It can't be REQUEST, because REQUEST scope only lasts across local invocations, and the FruitStore and OrderConfirm interfaces are remotable. It's awkward to make it CONVERSATION, because this would require us to introduce a conversational interface. The provider controls the definition of the FruitStore and OrderConfirm interfaces, so the client can't make either of those conversational. The client could make the interface by which it's invoked conversational, but this seems wrong because this scenario doesn't require statefulness between the client and its invoker, only between the client and its callbacks. A possible solution would be to introduce a new scope (e.g., STATEFUL or CALLBACK), which causes callbacks to be routed to the same stateful instance that initiated the forward call. When an invocation on a STATEFUL scoped instance returns, the instance would be released. This solves the problem for synchronous callbacks (as in this example) but not if the callbacks are asynchronous. The only way to handle the possibility of asynchronous callbacks seems to be to use case 1 and design this into the provider interface. I'd appreciate comments on any of this, especially on the final twist of the plot. Are there any other solutions to the case 2 scope problem? If not, is it worth adding the mechanisms to support case 2 if this can only handle synchronous callbacks? Simon
[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]