Convesations with Vorpal

In my last blog, I talk about using Vorpal with CDI. As you probably know, one of the responsibilities of CDI is to manage the lifecycle of Java objects within a web application for example. All the examples shown from that blog were request scoped.

In this blog, I’ll talk about using conversation with CDI. I’ve talked abit about conversation support here. I’ll expand on those concepts with regards to CDI.

How to Hold A Conversation

Every XMPP <message> packet that has the <thread> element is capable of supporting conversation in Vorpal.  See this if you are not familiar with conversation. Let look at how to use conversation by way of the shopping cart use case. 

We have a shopping client that will send items that we wish to purchase to cart@some_XMPP_domain.org. The items are carried in a separate XML element (uri:items) in a <message> packet. On receiving such a message, our shopping cart service will see if a conversation has been initiated; if not, the service will start a conversation. It will also create a ShoppngCart object and save that in the ConversationContext

The following class shows how this is done:

@ConversationScoped
@Message
@To(“cart@{__domain__}”)
public class ShoppingCartHandler {
   @Inject @Named(“__conversation__”) Conversation conv;
   @Inject ThreadID threadId;
   @Inject ConversationContext ctx;

   @XmlElement(“uri:items”, parameter=”{item}”)
   private List<Object> shopping(@Bind(“{item}”) Items items) {
      ShoppingCart cart;
      if (conv.isTransient()) {
         //Starts the conversation
         conv.begin();
         //Create the shopping cart
         ctx.setAttribute(“shopping-cart”, new ShoppingCart());
      }
      cart = (ShoppingCart)ctx.getAttribute(“shopping-cart”);
      // Save items into cart – not shown
   }
}

So to support conversation, annotate the class with with one of these annotations

  • javax.enterprise.context.RequestScoped
  • com.kenai.jabberwocky.framework.ConversationScoped, not javax.enterprise.context.ConversationScoped
  • javax.enterprise.context.ApplicationScoped – the use of this annotation this is not recommended. Will explain later

Secondly inject one of these into the message handler

  • Conversation – this allows us to start and terminate a conversation. Furthermore, when you are injecting Conversation, you must qualify it with @Named(“__conversation__”). This is to avoid confusing the container into injecting the default JSF conversation. The usage of this class is exactly the same as JSF.
  • ThreadID – this will give you the thread id from <thread> element in your message
  • ConversationContext – this is very similar to HttpSession. It allows data pertaining to the conversation to be saved like our ShoppingCart object; also all message handles/objects in a session will have access to the same ConversationContext thereby facilitating data sharing.

When ShoppingCartHandler.shopping() fires (see this on method resolution), we first checks to see if the conversation is transient; if it is, then we start the conversation, creates a new instance of ShoppingCart object and save that in our ConversationContext. ConversationContext are persistent and is bound to that conversation once its started.

Starting a conversation cause all classes that are annotated with ConversationScoped to be persisted in that conversation context. When a conversation ends, all these cached objects will be cleared. RequestScoped objects created under a conversation are not persisted. Conversations do not need to be started in a ConversationScoped object; you can call Conversation.start() from within a RequestScoped or an ApplicationScoped message handler. In fact you can have a ‘conversation’ from all RequestScoped objects. ApplicationScoped objects are never cleared when a conversation ends.

Vorpal can also be used without CDI. In that case if you have started a conversation, then all message handlers fired during the conversation will be persisted and will only be released when the conversation ends.

Now let’s look at how we handle checkout:

@RequestScoped
@Message
@To(“cart@{__domain__}”)
public class CheckoutHandler {
   @Inject @Named(PredefinedBindings.CONVERSATION) Conversation conv;
   @Inject ConversationContext ctx;

   @Body(“checkout {*ignore}”)
   public String checkout() {
      ShoppingCart cart;
      if (conv.isTransient())
         return (“You have not done any shopping!”);
     
      cart = (ShoppingCart)ctx.getAttribute(“shopping-cart”);
      //Perform checkout
         …
      //Terminate the conversation – free all objects
      conv.end();
         …
   }

}

To perform a checkout, all you have to do is send a message with ‘checkout‘ as the first word in the message’s body; this will cause CheckoutHandler.checkout() to be fired. We first check if a conversation has been started, sending an appropriate message back to the client if not. We then get the ShoppingCart object from ConversationContext and perform the checkout. Finally we end the conversation by calling Conversation.end(). This will free ConversationContext and also all ConversationScoped object created during this conversation.

Attributes Reinjection

If you are familiar with CDI, once injection have been performed on persistent object like SessionScoped or even ApplicationScoped objects, their members are no longer reinjected. Persistent objects in Vorpal are reinjected whenever they fire. Lets look at a simple example

@ConversationScoped
@Message
@XmlElement(“uri:items”, parameter=”{items}”)
public class ModifyShoppingCart {
   @Inject @Body String body;
   @Inject @Named(PredefinedBindings.CONVERSATION) Conversation conv;
   …

}

In the above message handler, a new instance of ModifyShoppingCart cart will be created if a conversation is not started. So when we create a new instance of ModifyShoppingCart, we will inject the message’s body into body. If however a conversation have been started, then an instance of ModifyShoppingCart will be created and save in the conversation’s context; and it is this instance that will be reused for that particular conversation, if it fires again, until we terminate the conversation.

Unlike the default CDI injection semantics, whenever we reuse a message handler object from a conversation context, all injected attributes pertaining to the incoming message will be refreshed; for example, in ModifyShoppingCart, we are injecting the message body into body member. So whenever ModifyShoppingCart is fired again under the same conversation, body member will be injected with the latest value in <body> from the incoming message. This is also the reason why its a bad idea to have ApplicationScoped objects are message handles.

If you wish to try out conversation support, get the latest build from here. Let me know what you think. Till next time

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: