OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

sca-j message

[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]