Yet Another XMPP Client Library


Vorpal framework is an external component framework. You use it to build services to be delivered by your XMPP server. It occurred to me that I could use the same programming model for client connections viz. like Smack. So now let me introduce Vorpal Client.

Vorpal Client is a Java XMPP library that allows you to connect to an XMPP server; the framework can be used in 3 different ways depending on how much control you want to have.

  1. Implement the com.kenai.jabberwocky.framework.client.ClientComponent interface
  2. Extend the com.kenai.jabberwocky.framework.client.AbstractClientComponent class. The first and second method is very similar to Tinder where you either implement org.xmpp.component.Component or extend org.xmpp.component.AbstractComponent. These methods are quite low level in that you manipulate the XMPP packets directly.
  3. The third is to use CDI, like Vorpal. For the rest of this blog, we will be focusing on this third method. This mode uses the same programming model as Vorpal; the big difference is that whereas Vorpal runs in a container (GlassFish), Vorpal Client runs in an SE environment so there is a little bit of setup involved.

Lets look at the steps to create a simple application that connects to OpenFire (I’ll leave GoogleTalk as an exercise for the reader) using method 3 above.

Create a Configuration File

<connection port=”5222″ domain=”batcomputer”>
   <component-class name=”com.kenai.jabberwocky.framework.client.JabberwockyClientComponent”/>
   <classes-root>classes</classes-root>
   <packages>
      <package name=”com.acme.xmpp.client”/>
   </packages>
   <properties>
      <property
name=”vorpal.component.description” value=”A component to query the customer’s database”/>
      <property
name=”vorpal.component.name” value=”Customer Query Client”/>
   </properties>
</connection>

A few salient points regarding the configuration file

  1. The <connection> starts the configuration. You specify the XMPP server in domain attribute. port attribute is optional and will default to 5222.
  2. After specifying the connection, you now define the client component. If you’re using method 3, then you just use copy the line above.
  3. The <classes-root> and <packages> tells Vorpal Client where to look for your classes (annotated with @Message, @IQ and @Presence) and what packages it should scan. You only have to specify either <classes-root> or <packages>. .
  4. Finally you can specify properties for your components.

Create a Startup Class

Note: If you are not familiar with using CDI (Weld) in Java SE, have a look at this document before proceeding.

We need to first create a startup class.

public class Startup {
   //Inject the command line arguments
   @Inject @Parameters List<String> args;
   //Called when container starts up, like psv main()
   private void initialize(@Observes ContainerInitialized containerEvt) {
      …
   }
}

Next create a main class like so

public class Main {
   public static void main(String[] args) {
      new StartMain(args).go();
   }
}

StartMain is part of Weld SE environment. So you will need to download Weld and add weld-se.jar into your CLASSPATH. Compile the class and bundle it into a JAR file (myclient.jar). Don’t forget to add beans.xml in the META-INF directory.

To run it type the following

java -cp ${CLASSPATH}:myclient.jar Main jabberid password

Creating Connection using Vorpal Client

The following code snippet shows how to create a connection to XMPP server

public class Startup {
   //Inject the command line arguments
   @Inject @Parameters List<String> args;
   //Inject a client connection
   @Inject ClientConnection connection;

   //Called when container starts up, like psv main()
   private void initialize(@Observes ContainerInitialized containerEvt) {
      //Exceptions omitted
      //Read the configuration – see above
      ClientComponentConfiguration config = ClientComponentConfiguration.read(new File(“client.xml”));

      //Connect to the server specified in the config file
      connection = connection.connect(config);

      //Login to the server
      if (!connection.login(args.get(0), args.get(1)) {

         System.err.println(“Authentication failed!”);
         System.exit(-1);
      }
      //Create the application
      connection.createApplication(config);

   }
}

Some salient points regarding the code snippet from above

  • You can create ClientConnection manually by specifying the server and port; see javadoc. But if you are planning to get access to the ClientConnection object in other parts of your application, then its better to use CDI injection. The injected ClientConnection is application scoped so you’ll get the same object throughout your application. The current release does not supported @Named ClientConnections. @Named will allow the support of more than one injected connection. This is currently a very low priority cause I’m not sure if anyone will use more than 1 connection.
  • Next we read in the application’s configuration and use that to initialize the connection. Invoking connect() causes the connection to connect to the XMPP server.
  • Following that, we login to the server. Username and password is not part of the configuration so you have to somehow pass that in. In the example above, we pass it in through the command line. If the authentication fails, login() will return false
  • Finally we call createApplication() to create and start the application.

Initializing Your Application

Once you have got past the createApplication(), you can now start your application using Vorpal’s lifecycle events.

private void postComponentStart(@Observes ComponentPostStart postStartEvent) {
   //Exceptions omitted
   ResponseContext respContext = new ResponseContext();
   respContext.type(
ResponseContext.Type.Presence)
         .add(“I’m online”)
         .add(Presence.Show.chat);
   postStartEvent.getComponentContext.send(respContext);
}

In the example code above, after the ComponentPostStart event, we send a presence message using response context (see this if you are unfamiliar with response context). For Vorpal Client, if you do not fill in the to() and from() in the response context, then they will default to the server that your client is connect to and the Jabber ID that you’ve used to login respectively. There is also a new predefined binding call __self__. __self__ will give you the full JID of the Jabber ID that is used to login. For an application to find out what is the id that is used to login, you can do this

@Inject @Named(“__self__”) JID me;

While we are on this topic, __subdomain__ will return null and __domain__ will return the Jabber server that the application is connected to. There are also a few new annotations @Identity, @Feature and @Item to match disco#info and disco#items.

Your application can respond to incoming message in the same way that you are using Vorpal viz. their programming model is exactly the same so I won’t repeat it here. You can find an introduction to Vorpal programming model here.

Running the Application

The bad news is that Vorpal Client uses a quite a lot of external JARs. The good news is that most of these are already bundle for Jabberwocky. So lets assume that your application JAR is call myclient.jar, then this is how you run it

java -cp myclient.jar:vorpal.jar:weld-se.jar:jabberwocky_support.jar my.main.Class

You can get vorap.jar and jabberwocky_support.jar from the framework bundle. weld-se.jar from Weld download (use Weld 1.1). Please note that jabberwocky_support.jar MUST BE LAST JAR in the -cp switch because of this issue.

I have a full blown Vorpal Client application here if you need more example. The QueryClient complements the CustomerQuery; the source can be found here.

Vorpal Client currently is still considered experimental and does not support features like compression SSL, etc.

The latest bundle that supports Vorpal Client can be found here. There are lots of bug fixes to the Vorpal framework especially if you’re using CDI. So please upgrade to this release.

I’m keen to hear what you think of Vorpal Client as I’ve big plans for it. The really nice thing about it is that any feature that I add to the client can also be used in the ‘server’ portion. 

Till next time.

Lifecycle, Handling Errors, Service Discovery Registration and Response Context

In this blog I’ll talk about 4 enhancements that I’ve recently added to Vorpal. These are not real game changer and if you do not use any of these, its no big deal. However having said that it does make using Vorpal a little easier.

Lifecycle

The first of these enhancement is lifecycle viz. you are able to listen to your component’s lifecycle and perform initialization or cleanup operations. There are 4 stages and they occur in the following order

  1. Pre start – before Vorpal makes a connection to the server
  2. Post start – occurs after a connection have been made to the server. This is also the first time you can send a packet out. We have not entered the application yet
  3. Main application loop – this is not really an event but the stage where your Vorpal application starts to receive packets
  4. Pre shutdown – occurs just before we are disconnected from the server. This is also the last time you can send out a packet. No received packets will be routed to your application
  5. Post shutdown – your Vorpal application is no longer connected to the server

You have to use CDI events to capture the lifecycle events. The following code shows how this is done

@RequestScoped
public class Lifecycle {
    private void preStart(@Observes ComponentPreStart preStartEvent) {       
    }
    private void postStart(@Observes ComponentPostStart postStartEvent) {
    }
    private void preShutdown(@Observes ComponentPreShutdown preShutdownEvent) {
    }
    private void postShutdown(@Observes ComponentPostShutdown postShutdownEvent) {
    }
}

Note the Java class to observe for a particular lifecycle stage. The event objects allow you to

  • statically register identities, feature and items. We will cover this in later in this blog
  • get a reference to the component, component manager, the JID of your component, the subdomain, etc.
  • get and set properties
  • send packets

Error Handling

Instead of manually creating an error packet, Vorpal lets you indicate error by returning either PacketError.Conditon and/or PacketError.Type. Below is an example from handleQuery() from CustomerQueryHandler.

@Body("{body}") 
public List<div class="youtube-video"><object> handleQuery(@Named("body") int custId) {   
   List<div class="youtube-video"><object> result = new LinkedList<div class="youtube-video"><object>();
   Customer customer = em.find(Customer.class, custId);
   if (null == customer) {
      result.add("Customer not found: " + custId);
      //Adds error condition to the return packet      
      result.add(PacketError.Condition.not_acceptable);   
   } else {      ...   }   
   return (result); 
}

Since we cannot find the custId, we return a message packet with the appropriate error condition. The above would result in the following packet<message to="fred@batcomputer/pidgin" from="query@customer.batcomputer"

<message to="fred@batcomputer/pidgin" from="query@customer.batcomputer<br />      type="error"><br />   <body>Customer not found: 456</body><br />   <error code="406" type="modify"><br />      <not-acceptable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/><br />   </error><br /></message>

Of course you can also just return a PacketError object.

Service Discovery Registration

You can now statically register identity, feature and item if these need not be dynamically generated. By default, all Vorpal component will return the following in response to a disco#info

<identity category='component' type='generic' name='the name of your component'/><br /><feature var='http://jabber.org/protocol/disco#info'/><br /><feature var='urn:xmpp:ping'/><br /><feature var='jabber:iq:last'/><br /><feature var='urn:xmpp:time'/>

There are no items return by disco#items. You can completely remove, add or change these default identity and feature via static registration. To modify these, you need to get a reference to ComponentContext. You can get it by the following 2 ways

  • From the lifecycle event ComponentPreStart, ComponentPostStart, ComponentPreShutdown and ComponentPostShutdown
  • By injection. There are 2 ways of doing this
    • @Inject ComponentContext
    • @Bind(“__componentContext__”) ComponentContext. You can also use @Named like so @Inject @Named(“__componentContext__”) ComponentContext

Lets look at an example of how you can statically register identity, feature and items

private void preStart(@Observes ComponentPreStart preStartEvent) {
   ComponentContext context = preStartEvent.getComponentContext();
   context.identity(new IdentitySpecification(“component”, “bot”, “Customer Query”));
   context.add(new FeatureSpecification(“uri:customer_query”);
   context.add(new ItemSpecification(“info@{__subdomain__}“);
}

In the above code example, you’ll notice that we are adding (appending) a feature and an item to our component. You can use ‘capture’ when you are specifying items; as you can see, when we are adding info entity we do not know what is the final subdomain name is, so we use the capture {__subdomain__} or the constant PredefinedBindings.PARAMETER_SUBDOMAIN. Vorpal will perform the substitution for you.

If you want to completely remove the default identity and features then use ComponentContext.identity() and ComponentContext.feature() respectively to set new identitys and features. If we return to the example above, you will notice that we use identity() which means that we are deleting the existing identity list (component/generic) and replacing that with component/bot.

The static registration works in combination with dynamically generated disco#info and disco#item to produce the final service discovery packet. Dynamically generated here means that you have defined a disco#info or disco#items handler (see this blog and this screencast). If you want only want to dynamically generate your disco#info and disco#items then define a handler service discovery and delete the default identities and features like so

context.identity();
context.feature();

Response Context

Before going into response context, let me explain a few concepts regarding responses. Look at the following code snippet

@Body(“{body}”)
public String handle(@Named(“body”) String body) {
   if ((null != body) && (body.trim().length() > 0))
      return (body.toUppercase());
   return (“Whats up?”);
}

Its a very simple message handler; when we return the result String, Vorpal will create the reply back according to the original packet. You can in fact return a number Java objects like JAXB objects, DataForm, Presence.Show.chat, Map, etc. Depending on the received message, Vorpal is able to construct a correct packet to send back to the recipient; eg a received Message will result in Message, a IQ get will result in IQ set, etc. The response context allows Vorpal to deal with what you return correctly.

You can also return a Packet; if you do that, then Vorpal will not perform any processing on the Packet but will just send that out. One of the disadvantages of using a Packet is that there are lots more things for you to do for example, filling in the recipient, creating the query child, etc. Furthermore, if you are sending multiple message then you have to creating multiple Packet; the default way of returning Java object does not work as Vorpal it assumes you only want to send 1 packet back to the recipient.

The response context API is a simple Java class that gives you the user friendliness of leveraging on the response context and the flexibility of constructing of a Packet yourself. It purpose is firstly to solve working with Packets directly and secondly to allow you to work with any of the following packet exchange patterns:

  • one packet to one recipient – one reply back to the same sender
  • many packet to one recipient – multiple reply (different packets) back to the same sender
  • one packet to many recipient – one reply back to multiple recipients, including the sender
  • many packet to many recipient – multiple reply back to multiple recipients, including the sender

Lets look at how we go about doing this

One packet to one recipient

This is the default case. Any thing that you return will be send back to the recipient; the recipient here meaning the sender of the packet that has caused your message handler to fire. In cases where a packet is not expected like a Presence handler, you have to use a Packet to override the response context. You can now return a ResponseContext object instead. 

@PresenceType(Presence.Type.chat)
public Object sayHello(@From JID from) {

   ResponseContext responseContext = new ResponseContext();
   responseContext.type(ResponseContext.Type.Message)
         .add(“Hello ” + from);

   return (responseContext);
}

If we did not use ResponseContext, then Vorpal will interpret whatever you return based on the response context which is a presence packet. We did not need to add the recipient’s name to responseContext because whatever information that is missing, Vorpal will fill that in from the originating message.

Many packet to one recipient

In some situation you would like to send multiple messages back to the recipient. Look at the following message handler from CustomerServiceDiscovery example which you can find in the playground

@To(“query@” + PredefinedBindings.PARAMETER_SUBDOMAIN)
@Query(PredefinedBindings.DISCO_INFO)
public List
queryDiscoInfo() {
   List<Object> result = new LinkedList<Object>();
   result.add(new IdentitySpecification(“client”, “bot”));
   result.add(Constants.CUSTOMER_QUERY);

   //Create a second packet to send a instruction as message
   //Remember to turn off bot sentry if you are using Pidgin
   ResponseContext responseContext = new ResponseContext();
   responseContext.type(ResponseContext.Type.Message)
         .add(“Customer query components allows you to query database by entering the customer id”);
   result.add(responseContext);

   return (result);
}

Notice that we first construct our reply (order is not important) to a disco#info; the reply is shown in green. We then construct a second packet using ResponseContext API. This is shown in blue. We say that the second message is going to be a XMPP message, then we add a String to the responseContext object. This is exactly how we would have done had this been in response to a XMPP message. We then add the responseContext to or reply.

Vorpal will now process this and send off 2 packets, one IQ reply and the other a Message.

The result is that whenever you perform a disco#info on query@some_subdomain, queryDiscoInfo() will send back 2 packets; the first is a response to the disco#info and the second is an XMPP message telling the entity who send the disco#info how to perform a query.

The following is a equivalent to the above method

@To(“query@” + PredefinedBindings.PARAMETER_SUBDOMAIN)
@Query(PredefinedBindings.DISCO_INFO)
public List queryDiscoInfo() {
   List<ResponseContext> result = new LinkedList<ResponseContext>();
   ResponseContext responseContext = new ResponseContext();
   responseContext.add(new IdentitySpecification(“client”, “bot”));
   responseContext.add(Constants.CUSTOMER_QUERY);
   result.add(responseContext);
   //Create a second packet to send a instruction as message
   //Remember to turn off bot sentry if you are using Pidgin
   responseContext = new ResponseContext();
   responseContext.type(ResponseContext.Type.Message)
         .add(“Customer query components allows you to query database by entering the customer id”);
   result.add(responseContext);
   return (result);
}

In the first responseContext instance we did not even tell it the message type; since message type is missing, Vorpal will set that to be a IQ reply message.

You cannot add a ResponseContext instance to another ResponseContext instance in other words you cannot embed ResponseContext within a ResponseContext.

One packet to many recipient

If you wish to send the same packet to multiple recipient with ResponseContext, you need to fill in the to() method. Once you have filled in the to(), Vorpal will not insert the recipient, so do keep that in mind. The following code snippet sends out multiple Presence message

ResponseContext presence = new ResponseContext();
presence.type(ResponseContext.Type.Presence)
      .to(“fred@bedrock”, barney@bedrock”, “holmer@springfield”)
      .add(Presence.Show.chat)
      .add(“I’m available”);

Many packet to many recipient

Finally in many to many pattern, just return a Collection of ResponseContext and each ResponseContext have multiple recipient. The ResponseContext can be of different message type.

Let me know what you think of these enhancements to Vorpal. You can find the latest bundle here.

Hello?

In XMPP, presence allows a person/bot/service to tell others about its availability. The latest release of Vorpal now supports presence; major annotations include the followin

  • @Presence – top level annotation to denote a class is a presence message handler
  • @PresenceType – type of the presence packet; see this
  • @Priority – priority of presence packet
  • @Show – availability. See this
  • @Status – the status of the presence packet

Lets see how we handle presence from an example. Remeber query@customer.batcomputer from the service discovery blog? Let say that after we have discovered query@customer.batcomputer, the user wants to add query to his/her roster. The following are the series of message exchanges between the user and the Customer Service.

The user sends a subscribe message, indicating that the user wants to receive query’s presence info

<presence to=”query@customer.batcomputer” from=”…” type=”subscribe“/>

If query@customer.batcomputer decides to accept the subscription, it’ll return a presence message with subscribed

<presence to=”fred@batcomputer/pidgin” … type=”subscribed“/>

Note: In most cases, there is also a reverse subscription send out viz. query@customer.batcomputer will also send a subscribe to fred@batcomputer. However in this use case query@customer.batcomputer is not interesed in fred@batcomputer‘s availability.

Once query has accepted the presence subscription of fred, the next message that query is going to get is most likely to be a probe message viz. enquiring about the presence status

<presence to=”query@customer.batcomputer” … type=”probe“/>

query can reply in the following way

<presence to=”fred@batcomputer/pidgin” …>
   <show>chat</show>
   <status>Available for customer enquiry</status>
   <priority>5</priority>
</presence>

assuming it is available.

We will now implement the above set of interactions with Vorpal.

@RequestScoped
@Presence
@To(“query@{__subdomain__}”)
public class PresenceHandler {
   //Method to handle subscription
   @PresenceType(type=org.xmpp.packet.Presence.Type.subscribe)
   public Object handleSubscription() {
      return (org.xmpp.packet.Presence.Type.subscribed);
   }
   …
}

  • Firstly presence handlers must be annnotated with @Presence annotation.
  • The @To is really optional; in here we are really particular in that we only want to entertain subscription request to query@customer.batcomputer.
  • Another point here is the use of @PresenceType to discriminate the packet; as you can see from the example above, when there is a subscription request handleSubscription() will fire and reply with a subscribed.

After accepting the subscription, we will now need to handle probe messages. The following handler shows how this is done

   @PresenceType(type=org.xmpp.packet.Presence.probe)
   public List<Object> handleProbe() {
      List<Object> result = new LinkedList<Object>();
      //Available to ‘chat’
      result.add(org.xmpp.packet.Presence.Show.chat);
      result.add(“Available for customer enquiry”);
      result.add(5); //Priority
      return (result);
   }

Before I conclude this blog, I would like to add that any subscription to the component is not handle by the XMPP server viz. fred@batcomputer subscription to query@customer.batcomputer should be handle by the component (CustomerService). In the example above you should probably save the senders JID if you want to do something with it later eg. notifying them that the service is going away when you are shutting the service down. Here is the modifed presenceHandler() with the sender’s JID

   //Method to handle subscription
   @PresenceType(type=org.xmpp.packet.Presence.Type.subscribe)
    public Object handleSubscription(@From JID from) {
      //Persist sender
         …
      return (org.xmpp.packet.Presence.Type.subscribed);
   }

Vorpal supports component lifecycle event which allows you to send presence info to your subscribers. I’ll blog about lifecycle and other miscellaneous features in an upcoming blog. For the impatient, see this as an example of using lifecycle events.

The latest build of the project is available here.

Till next time

XMPP Forms

Up till now, all my examples pertaining to exchanging data between the client and the external component have been using <message>. However there is a more structured way of exchanging data using forms (XEP-0004) very much like HTTP forms. The XMPP forms has a very rich structure; the following points provide a quick overview of XMPP data forms
  • firstly all the fields in the form are typed (or not, if you so choose); types includes ‘primitives’  like String, JID, boolean. Field can have attributes like hidden, fixed (label) and password. Furthermore fields can also be multivalued viz. a collection. The following shows an example of a XMPP form

<x xmlns=’jabber:x:data’ type=’form’>
   <title>Bot Configuration</title>
   <instructions>Fill out this form to configure your new bot!</instructions>
   <field type=’hidden’ var=’FORM_TYPE’>
      <value>jabber:bot</value>
   </field>
   <field type=’list-multi’
         label=’What features will the bot support?’ var=’features’>
      <option label=’Contests’><value>contests</value></option>
      <option label=’News’><value>news</value></option>
      <option label=’Polls’><value>polls</value></option>
      <option label=’Reminders’><value>reminders</value></option>
      <option label=’Search’><value>search</value></option>
      <value>news</value>
      <value>search</value>
         …
</x>

  • each form is denoted by a “type” which defines the semantics of the form. The type can be form, submit, cancel and result. In the example above, we see that the type is ‘form’; this means that the party that is submitting the form is asking the receiver to fill in the form.
  • XMPP forms can also contain multiple items; for example when you submit a Google search, Google will list multiple results in a page. All these result items contain the same structure like a text heading, a URL, a short description of the result. XMPP form can model this structure. A form of this type consist of 2 parts: a ‘header’, between a pair of <reported> tags, contains the structure of each <item> and the item itself with just the values. The following is an example of this type of form

<x xmlns=’jabber:x:data’ type=’result’>
   <title>Google Search: verona</title>
      <reported>
         <field var=’name’/>
         <field var=’url’/>
      </reported>
      <item>
         <field var=’name’>
            <value>Comune di Verona – Benvenuti nel sito ufficiale</value>
         </field>
         <field var=’url’>
            <value>http://www.comune.verona.it/</value&gt;
         </field>
      </item>
      <item>
         <field var=’name’>
            <value>benvenuto!</value>
         </field>
         <field var=’url’>
            <value>http://www.hellasverona.it/</value&gt;
         </field>
      </item>
         …
</x>

  • A XMPP form can be embedded into any of the 3 XMPP packets; the implication of this is that a XMPP form can be used in many different context for example a form can be used in an IQ packet to configure a MUC room or in a <message> packet to solicit details from a user

@DataForm Annotation

Vorpal allows you to manipulate data forms very easily through @DataForm annotation together with some powerful mapping capabilities. There are 2 use cases: the first is unmarshalling/unserializing a data form from a packet and the second is creating a data form to be returned as result.

Processing DataForm

Lets look at capturing a data form from an incoming packet. Assume we have the following incoming packet

<iq id=”F80xW-15″ to=”query@customer.batcomputer” type=”get“>
   <query xmlns=”uri:customer_query“>
      <x xmlns=”jabber:x:data” type=”form”>
         <field var=”customerId” type=”text-single”>
            <value>1</value>
         </field>
         <field var=”name” type=”text-single”/>
         <field var=”addressline1″ type=”text-single”/>
         <field var=”addressline2″ type=”text-single”/>
         <field var=”city” type=”text-single”/>
         <field var=”zip” type=”text-single”/>
         <field var=”state” type=”text-single”/>
         <field var=”phone” type=”text-single”/>
         <field var=”fax” type=”text-single”/>
         <field var=”email” type=”text-single”/>
         <field var=”discountCode” type=”text-single”/>
         <field var=”creditLimit” type=”text-single”/>
      </x>
   </query>
</iq>

In the packet example shown above, the packet contains a data form in <query>. The idea here is that we have an empty form except the customerId field. query@customer.batcomputer service will lookup the customer id (customerId field), from the database, fill in the form and return it to the submitter (data form type is form). The following code shows how we might handle this packet

@RequestScoped
@IQ
@Query(“uri:customer_query”)
public class ProcessQuery {
   @PersistenceContext EntityManager em;

   @Field(name=”customerId”, parameter=”{custId}”)
   public Object process(@Bind(“custId”) int custId) {
      Customer customer = em.find(Customer.class, custId);
      //etc. etc
   }

Lets go through the code

  1. we have a @Query (see disco blog) annotation which handles only uri:customer_query namespaced query.
  2. Next we have a @Field annotation which only fires process() method if the data form has a field call customerId. By using a @Field annotation we are assuming that the packet has a data form. Vorpal will look for a XMPP data form in the ‘usual’ places; in <iq> that ‘usual’ place translate to inside the child of <iq> packet which in this example will be in <query>. For <message> and <presence>, Vorpal will look for a data form as a direct child of <message> and <presence> packets. Any other location you’ll have to use XmlPath annotation, which is an annotation for XPath, from com.kenai.jabberwocky.framework package

Instead of using @Field to fire process(), you can also use @DataForm annotation:

   @DataForm
   public Object process() {

process() will fire if there is a data form in the packet in the ‘usual’ place. Of course that example complete ignores how we get the customer’s id which will be the subject of our next section.

Unmarshalling DataForm

There are 3 ways that you can get a data form into your message handler; the first and simplest way is to simply inject it into a DataForm object. The following is an example of how you do this

@RequestScoped
@IQ @Query(“uri:customer_query”)
public class ProcessQuery {
   @PersistenceContext EntityManager em;  
   @DataForm org.xmpp.forms.DataForm customerForm;

A second method is to inject the form into a Map (map injection). As you might have guessed, the keys of the Map are the form’s name, and the value(s) are converted into their default Java type based on the field type. The following table shows this conversion.

DataForm type Default Java type
boolean java.lang.Boolean
fixed fixed fields are ignored. Will also not appear in map injection and form binding (see below)
hidden See text-single below
jid-single org.xmpp.packet.JID
text-single java.lang.Boolean if it is ‘true’/'false’, java.util.Date if we can parse it, java.lang.Long or java.lang.Double if it is a ‘numeric’ string, java.lang.String otherwise
text-private char[] viz. an array of character. Inspired from JPasswordField
jid-multi java.util.Collection<JID>
list-multi java.util.Collection<Object> where Object will follow the conversion rule of text-single
text-multi java.util.List<String>
list-single As text-single

If the field type is missing, Vorpal will try to guess. So you can inject a data form into a Map like so

@DataForm Map<String, Object> customerForm;

or

//Using the non generic Map which is exactly the same as above
@DataForm Map customerForm;

You can also use Map<String, String>; in this case all the values will all be String. For ‘multi’ field types like text-multi, list-multi, jid-multi, the value will be a comma separated list of the field values. Besides injecting the form values into a Map, you can also get the form field’s type by specifying FormField.Type as the second parameterized type in Map. The following example shows how forms injection works

//Map the value
@DataForm Map<String, Object> customerForm;
//Map the field type
@DataForm Map<String, FormField.Type> fieldType;

for (String fieldName: fieldType.keySet()
   System.out.println(“Field name: %1$s, value: %2$s, type: %3$s”
         , fieldName, customerForm.get(fieldName), fieldType.get(fieldName));

A third way to read the value from a data form is to use forms binding. Forms binding is very similar to JAXB in that you map a data form into a Java object. The following Java object creates a binding to our customer query form example shown above

@DataForm
public class Customer {
   …
   public int getCustomerId() { …
   public void setCustomerId(int id) { …

   @FormField(name=”addressLine1″)
   public String getAddress1() { …
   public void setAddress1(String addr1) { …

   …
}

  • Firstly annotate your Java object with @DataForm to tell Vorpal that you want to use this class to bind (marshall/unmarshall) to a form. Vorpal will then try to match your JavaBean property names with the field name and copy the values from the form to the instance or vice versa. Type conversion is based on the type of the form
  • If for some reason, you need to override the above behaviour, annotate the JavaBean accessor with @FormField providing the field name that we should bind to and optionally the type
  • At the moment forms binding does not have an equivalent @XmlTransient to ignore a property

Now to bind Customer to a data form, do the following

@DataForm Customer customerForm;

Marshalling DataForm

After you have process the customer query, you’ll now need to return the form to the sender. Again the simplest way to do this is to create a DataForm object. If you do not wish to manipulate DataForm directly, Vorpal has a utility class call DataFormBuilder. Assume you would like to create the following data form

<x xmlns=”jabber:x:data” type=”form”>
   <field var=”customerId” type=”text-single”>
      <value>1</value>
   </field>
   <field var=”name” type=”text-single”>
      <value>JumboCom</value>
   </field>
   <field var=”address” type=”text-multi”>
      <value>111 E. Las Olas Blvd</value>
      <value>Fort Lauderdale</value>
      <value>33015 FL</value>
   </field>
   …
</x>

you perform do the following

DataFormBuilder builder = DataFormBuilder.create(DataForm.Type.result);
builder.field(“customerId”).textField(“1″)
      //The following is a longer version of .textField()
      .field(“name”).type(FormField.Type.text_single).value(“JumboCom”)
      .field(“address”).textField(“111 E. Las Olas Blvd”, “Fort Lauderdale”, “33015 FL”);
DataForm result = builder.build();

DataFormBuilder uses the fluent style API so using it is quite straightforward. If your form contains multiple result, DataFormBuilder handles that as well

DataFormBuilder builder = DataFormBuilder.create(DataForm.Type.result);
//Build the ‘header
builder.startReported()
      .field(“customerId”).type(FormField.Type.text_single)
      .field(“name”).type(FormField.Type.text_single)
      .field(“address”).type(FormField.Type.text_multi)
      //Other headers…
.endReported();

//For each row
builder.startItem()
      .field(“customerId”).value(“1″)
      .field(“name”).value(“JumboCom”)
      .field(“address”).values(“111 E. Las Olas Blvd”, “Fort Lauderdale”, “33015 FL”)
      …
.endItem();

//Next row
builder.startItem()
      .field(“customerId”).value(

You can also return a Map as your result. This is the reverse of the marshalling process that I’ve described above. If you return multiple result, then return a list (or any Collection) of Maps like so List<Map<String, Object>> or just List<Map>.

Finally results can also be returned using forms binding. Again if you returning multiple result using forms binding, then return a list of your objects eg. List<Customer>

Lets complete our ProcessQuery handler above using forms binding to return the result

@RequestScoped
@IQ @Query(“uri:customer_query”)
public class ProcessQuery {
   @PersistenceContext EntityManager
   @Field(name=”customerId”, parameter=”{custId}”)
   public Object process(@Bind(“custId”) int custId) {
      Customer cust = em.find(Customer.class, custId);
      if (null == cust)
         return (ErrorPacketBuilder.create(Condition.item_not_found));
      //Vorpal will default form to result if type is not specified
      return (cust);
   }
}

Since Customer supports both JPA and forms binding we must make sure that it has the appropriate annotations

@Entity // for JPA
@DataForm
public class Customer {
   …

Download the latest bundle here (look for “Latest XMPP Project” entry) if you intend to try out Vorpal.

Till next time

Screencast: Developing Discoverable XMPP Components with Vorpal

A screencast explaining how disco#info and disco#items works in XMPP. There is also a ‘live’ coding show how to use @IQ and @Query nnotation.

Comments and feedback are welcomed.

Discoing with Vorpal

One of the really nice XMPP feature is service discovery viz. the ability for the client to discover the capabilities and services (eg. multiuser chat, pubsub, file transfer, etc.) offered by the server. The services discovery mechanism is described in great detail in XEP-0030. As far as specification goes, XEP-0300 is surprisingly a well written and quite easy to read.

In a nutshell, XEP-0030 works using 2 XML stanza

  • disco#info – tell me about yourself. This includes the server’s identity and the features that it supports
  • disco#items – give me a list of all your services or Jabber entities

I’ll show how service discovery works by using my customer query example (cross selling!). In the example, a client can perform customer information query by sending a <message> with the customer’s id in the <body> of the message to query@customer.batcomputer. What service discovery does is allows it allows query@customer.batcomputer to be discovered via a series of interactions.

But first the setup

  • We will use the the following URI uri:customer_query to denote the customer query service. So if a Jabber entity has this URI associated with it, then that entity supports our customer query service
  • The entity query@customer.batcomputer must be discoverable; the entity must also have uri:customer_query associated with it
  • The customer query service, an external component (XEP-0114), is deployed in the following subdomain: customer.batcomputer. So batcomputer is our XMPP server, customer.batcomputer is a subdomain of the server and query@customer.batcomputer is a JID entity within the customer.batcomputer subdomain.

Looking for Customer Service

The process is very simple; the client will look for a Jabber entity with uri:customer_query associated with it. The client starts by first sending a disco#info to batcomputer

<iq type=’get’ to=’batcomputer’ from=’client@batcomputer/pidgin’…>
   <query xmlns=’http://jabber.org/protocol/disco#info’/&gt;
</iq>

batcomputer then replies with a little info about itself and a list of services that it supports

<iq type=’result’ to=’client@batcomputer/pidgin’ from=’batcomputer’ …>
   <query xmlns=’http://jabber.org/protocol/disco#info’&gt;
      <identity category=’server’ …/>
      <feature var=’vcard-temp’/>
      <feature var=’http://jabber.org/protocol/disco#items’/&gt;
         …
   </query>
</iq>

The client then looks through the <feature> list to see if the server supports uri:customer_query. It doesn’t; it will now look to see if disco#items is supported. A disco#items indicates that  batcomputer has more jabber entities, so the client now sends a disco#items over to retrieve the list of entities

<iq type=’get’ to=’batcomputer’ from=‘client@batcomputer/pidgin’ …>
   <query xmlns=’http://jabber.org/protocol/disco#items’/&gt;
</iq>

batcomputer now returns a list of entities including customer.batcompter. For customer.batcomputer entity to be listed in the response, the customer service external component must be ‘up and running’.

<iq type=’result’ to=’client@batcomputer/pidgin’ from=’batcomputer’ …>
   <query xmlns=’http://jabber.org/protocol/disco#items’&gt;
      <item jid=’pubsub.batcomputer’ …/>
      <item jid=’conference.batcomputer’ …/>
      <item jid=’customer.batcomputer’ …/>
         …
   </query>
</iq>

The client repeats the above process for each <item>. If an entity does not support disco#items, it means that that entity is a ‘leaf’ node; no further introspection is possible. So when the client sends a disco#info to customer.batcomputer the following stanza is returned

<iq type=’result’ to=’client@batcomputer/pidgin’ from=’customer.batcomputer‘ …>
   <query xmlns=’http://jabber.org/protocol/disco#info’&gt;
      <identity category=’component’ …/>
      <feature var=’http://jabber.org/protocol/disco#items’/&gt;
         …
   </query>
</iq>

On not finding uri:customer_query, the client now sends a disco#items to customer.batcomputer; our customer service component now returns the following list of entities

<iq type=’result’ to=’client@batcomputer/pidgin’ from=’customer.batcomputer‘…>
   <query xmlns=’http://jabber.org/protocol/disco#items’&gt;
      <item jid=’query@customer.batcomputer’ …/>
   </query>
</iq>

A disco#info now goes out to query@customer.batcomputer. Here is the reply

<iq type=’result’ to=’client@batcomputer/pidgin’ from=’query@customer.batcomputer‘ …>
   <query xmlns=’http://jabber.org/protocol/disco#info’&gt;
      <identity category=’client’ type=’bot’/>
      <feature var=’uri:customer_query’/>
   </query>
</iq>

We have now found the entity that supports customer query!

Handling disco#info and disco#items with Vorpal

Now that we seen how service discovery works, lets implement this with Vorpal. The following table shows the request and response to and from our customer service

Request – client to customer.batcomputer Response – customer.batcomputer to client
1) Sends disco#info
Indicate support for disco#items in response
2) Sends disco#items
Returns query@customer.batcomputer entity
3) Sends disco#info to query@customer.batcomputer
Response with identity and support for uri:customer_query

We will show how to capture the above set of interactions with Vorpal.

@RequestScoped
@IQ @Query
public class CustomerServiceDiscovery {
   @Query(“http://jabber.org/protocol/disco#info&#8221;)
   public Object discoInfo() {
      return (http://jabber.org/protocol/disco#items&#8221;);
   }
}

The above class CustomerServiceDiscovery, shows the first pair of request/response.

  • Note that @IQ is a top level annotation that marks this class as an IQ packet handler (the only other top level annotation for message handler is @Message). This is immediately followed by @Query annotation. This annotation assures us that this handler will only process IQ packets with <query>. Other type of IQ packets without <query> as a child will be ignored.
  • The @Query annotation on discoInfo() method has the disco#info namespace. What this means is that for the discoInfo() method to fire, the <query> element must have the namespace that matches http://jabber.org/protocol/disco#info. <query> elements with http://jabber.org/protocol/disco#items will not match this annotation.
  • When discoInfo() method is invoked in response to a disco#info query, it will return the disco#items namespace to indicate that it has Jabber entities. Besides returning a String, you can also return a URI, URL or a com.kenai.jabberwocky.iq.FeatureSpecification instance.

Now lets look at how we implement the second set of interaction were the client sends a disco#items.

@RequestScoped
@IQ @Query
public class CustomerServiceDiscovery {
   @Inject @Named(“__domain__”) private String domain;

   @Query(“http://jabber.org/protocol/disco#info&#8221;)
   public Object discoInfo() {
      return (“http://jabber.org/protocol/disco#items&#8221;);
   }

   @Query(“http://jabber.org/protocol/disco#items&#8221;)
   public Object discoItems() {
      return (new ItemSpecification(“query@” + domain
            , “Customer Query Service”)
);
   }
}

  • The discoItems() method is used to handle disco#items request. Again we use a qualified @Query annotation to filter out other types of query packets
  • To return the correct subdomain, we inject the subdomain that this external component is deployed to to domain member. The __domain__ is a predefined name in Vorpal.
  • Finally in discoItem() we return an instance of com.kenai.jabberwocky.iq.ItemSpecification. The <item> element has an option for a user friendly string and also entry for node (see schema for disco#item here). In this example, we are setting the Jabber entity (the JID) and also a user friendly string. You can also just return a JID or a Stringify JID if you wish to be brief. If you have more than one entity, you can return a collection eg Collection<String>, List<JID>, Set<ItemSpecification>

The third set of interaction is a little different; since we have returned query@customer.batcomputer as an entity in the previous interaction, a disco#info will now be directed to is JID. The following code shows how to handle this.

@RequestScoped
@IQ @Query
public class CustomerServiceDiscovery {
   @Inject @Named(“__domain__”) private String domain;
   @Query(“http://jabber.org/protocol/disco#info&#8221;)
   public Object discoInfo() {
      return (“http://jabber.org/protocol/disco#items&#8221;);
   }
   @Query(“http://jabber.org/protocol/disco#items&#8221;)
   public Object discoItems() {
      return (new ItemSpecification(“query@” + domain
            , “Customer Query Service”)); 
    }

   @Query(“http://jabber.org/protocol/disco#info&#8221;)
   @To(“query@{__domain__}”)
   public Collection<Object> queryDiscoInfo() {
      List<Object> result = new LinkedList<Object>();
      result.add(new IdentitySpecification(“client”, “bot”));
      result.add(“uri:customer_query”);
      return (result);
   }
}

Salient point for this final set of interactions

  • We now add an additional annotation @To to indicate that queryDiscoInfo() will only fire if it is a disco#info for query@customer.batcomputer. The previous disco#info was to customer.batcomputer. See this for method resolution.
  • Note that the return type is a collection because we will be returning the identity of the Jabber entity (required by the spec) and also a list of features that the entity will support. In our case it’ll only be uri:customer_query.
  • The service chose to identify itself as a bot. See Service Discovery Identity for a list of predefined type and categories.

The latest build of Vorpal, Jabberwocky container and Vorpal NetBeans plugin can be found here.

Feedback appreciated.

Another Screencast – Stateful Objects with Conversation

I’ve really caught the screencast bug; here is another screencast on conversation support in Vorpal. In the screencast I use Vorpal to develop an Eliza service.

Here is how it works. IM users establish a chat session with eliza@arkham.batcomputer JID (or whatever subdomain you choose to deploy it in). If the in coming messages to Eliza has <thread> element, then Vorpal can use that to establish a conversation.

To begin a ‘therapy session’ with Eliza, send to eliza@arkham.batcomputer a message with start as the message content. Vorpal will create an instance of Eliza and hold that for the duration of the conversation. When you decided to end the session, simply send it a message with quit. Vorpal will clear all the objects in the conversation’s context.

Note: the above screencast is about 16:28 mins. Not sure why Youtube thinks its 49:18 mins

I’ve blogged extensively about conversation support and the programming model in my previous posting call Conversations with Vorpal.

You can get the source code of the demo here.
Get the latest Jabberwocky bundle here.
The Eliza source used in the screencast is available from http://chayden.net/eliza/Eliza.html.

Till next time.

Screencast – Developing XMPP Services with Jabberwocky and Vorpal

I’ve decided to move to the 21st century by recording my first screencast. After hunting and testing various Linux tools, I’ve settled on recordMyDesktop.

The following 2 screencast on this blog are my effort.

So what’s the screencast about? I’ve blog extensively about XMPP and the two framework that I’ve been working on, Jabberwocky and Vorpal. I thought it’ll be nice to show how to use these frameworks.

Part 1 of the screencast talks about the various ways you can develop XMPP services.
These includes service bots, server plugin and external component. The
second segment of this part describes a use case for querying a
database using XMPP.

In part 2 of the screencast, I will show how to implement the use case described above with Jabberwocky and Vorpal along with JavaEE services including CDI and JPA.

The source for the code from part 2 can be found here.

Please provide your comments and feedbacks. Till next time.

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

Integrating CDI with Vorpal and the Cuckoo Bird Effect


Nest with Cuckoo bird’s egg

I first came across the insidious practices of the Cuckoo bird in Clifford Stoll’s book The Cuckoo’s Egg. The Cuckoo bird is a brood parasite. They never build any nests of their own; so when its time for them to lay their eggs, they lay their eggs in the nest of other species (of bird) whose egg resembles that of their own. Unawares, the “foster” bird will feed the Cuckoo’s nestling when they hatch. In the book Cuckoo’s Egg, a hacker used a similar method to trick the host computer to give him root access through movemail.

Fascinating stuff you say but what does this piece of trivia have to do with Vorpal? Vorpal uses a similar technique, though for benign reasons, to coax Glassfish application server into giving it what it wants – JavaEE resources (object lifecycle management, JPA, transaction, Stateless Session Bean, validation, etc). Vorpal achieve this firstly by masquerading as a Web Application and secondly by integrating with CDI. The first allows a Vorpal application to be deployed into Glassfish and the second allowed the Vorpal applications to consume container resources.

In this blog, I will show you how you can use Vorpal with CDI. Note that the Vorpal programming model that I describe here is still valid. CDI supports builds on this. This blog is not a CDI tutorial. I’ll just be listing what are the features that are supported by Vorpal. Those of you who are not familiar with CDI are encouraged to go through this tutorial before continuing.

Message Handler Lifecycle

When you are using Vorpal with CDI, all Vorpal message handler class must be annotated with either @RequestScoped or @ApplicationScoped. The semantics of these 2 annotations are similar to those in a Java EE 6 application. Vorpal also supports a third lifecycle annotation call @com.kenai.jabberwocky.framework.ConversationScoped. This annotation is conceptually similar to @javax.enterprise.context.ConversationScoped but supports XMPP conversation semantics. As conversation support is quite involved, I’ll explain this in my next blog. Vorpal does not support @SessionScoped.

Once you have annotated with any one of the 3 annotations listed, you can also use lifecycle methods like @PostConstruct and @PreDestroy.

Here is a simple example of an object using CDI

@RequestScoped @Message
@Body(“Hello {*name}”)
public class HandleMessage {
   private final String name;
   @PostConstruct void init() {
      //Do something
   }
}

As in the example above, the HandleMessage class is a request scoped object meaning that a new instance of HandleMessage will be created when there is a match and HandleMessage is selected for firing.

Note the asterisk before name in @Bind. This is a new matching feature and it denotes none or more. If you specify “Hello {name}“, then a match will include a String literal following “Hello ” in the body like “Hello Fred Flintstone“. However with an asterisk, @Body will  match “Hello ” as well. See my previous blog for matching and firing.

Resource Injection

Vorpal supports full CDI resource injection. Injection points include

Furthermore all Vorpal annotations, eg. @Body, @Message, @XmlElement, @Bind, etc. are CDI qualifiers if they are use on fields and method parameters. If you are annotating members and methods, then the @Inject annotation is optional. There are also some predefined injection types like ComponentManager, Conversation, ThreadID and ConversationContext.

@RequestScoped @Message
@Body(“Hello {*name}”)
public class HandleMessage {
   private final String name;
   @From private JID from;
   @Inject @To private JID to; //@Inject is optional
   @Inject ComponentManager mgr; //Predefined type injection
   //Constructor injection
   @Inject public HandleMessage(@Bind(“name”)String n) {
      name = ((null == n) || (n.trim().length() <= 0))? “Fred”: n;
   }
   @PostConstruct void init() {
      //Do something
   }
   //Method injection
   @Inject void setSubject(@Subject String subject) {
      —
}

From the example above

  • Notice the member injection. Since all Vorpal annotations are CDI qualifiers, the @Inject annotation is optional
  • Vorpal defines a set of injectable objects which I’ve listed above. You can inject any of these types without qualifiers; as there are no qualifiers, you have to annotate the member with @Inject
  • You can injected any valid CDI resources into constructor, member and method
  • setSubject() is an example of method injection

Capture and @Named

Vorpal works by matching incoming XMPP messages against message handlers. Captures are bound to members and parameters by the @Bind annotation. See example above. If you are using Vorpal with CDI, you can continue to use @Bind to bind capture values; if however you are a CDI purist, you can use @Named instead. Every capture export themselves as @Named. These @Named objects have a slightly different semantics to the regular JavaEE @Named objects in the following ways:

  1. Capture @Named are local to the class – viz. they are not visible outside of the class
  2. Capture @Named are bound to the ‘nearest’ capture – viz. members and methods injection capture @Named are bound to class level capture and a method parameters capture @Named are bound that method capture

Here is a simple example to illustrate the point

@RequestScoped @Message
@Body(“Hello {*name}”) @From(“{from}@jabber.org”)
public class HandleMessage {
   private final String name;
   //Member level capture, bound to class
   //’name’ is only visible in HandeMessage class
   @Inject @Named(“name“) private String name;
  
   //Method injection – also bound to class
   @Inject void setFrom(@Named(“from“) String from) {
      —
   //name is bound to capture in @To
   @To(“{name}@{__domain__}”)
   public String handleMessage(@Named(“name“)String to) {
      …
   //name is bound to this method’s @To
   @To(“info-{name}@{__domain__}”) @Subject(“{subject}”)
   public String handleInfo(@Named(“name“) String name
         , @Named(“subject“) String subject) {
      …
}

Firstly notice that the @Named member (String name) and the @Named method (setFrom) parameter injection are bound to @Body and @From from the class level. Furthermore @Named are bound to their nearest capture. So @Named(“name”) in handleMessage method will be bound to the @To annotation on that method and not to @Body. Similarly for handleInfo, its @Named(“name”) will be bound to its @To annotation. If my explanation is a little confusing, you can see the various @Named association above from their colours. Another simple way of deciding which capture @Named is bound to which capture is to place your finger on the @Name, eg. on @Named(“name”) on handleInfo, and move up. The first capture with the same name that you encounter will be bound to that @Named. In this case, the it will be @To(“info-{name}@{__domain__}”).

One other point is that message handler methods are not annotated with @Inject. In the above example handleMessage and handleInfo are message handler methods because they do not have @Inject. If these methods were annotated with @Inject, then they would be invoked when HandleMessage is instantiated.

JavaEE 6 Resources

Once a message handler are under CDI control, the message handler can access all of the JavaEE’s platform resources via injection. I’ll not go into defining JavaEE resources like JPA, Stateless Session Bean, etc. The following code shows how you use JPA from within a message handler.

@RequestScoped @Message
@Body(“Hello {*name}”) @From(“{from}@jabber.org”)
public class HandleMessage {
   //Injecting EntityManager
   @PesistenceContext EntityManager em;
   private final String name;
   @Inject @Named(“name“) private String name;
   …

The latest bundle of Vorpal and Jabberwocky container can be found here. As of this blog, the latest bundle (Merdeka 2011) contains Glassfish containers, NetBeans plugin, Vorpal framework JAR (if you are not using NetBeans) and demo code. There are 2 demo code; the first Eliza demonstrate conversation support; however I feel that this is NOT a very good example and I’ll update it in the next release. The second is query, which basically shows using JPA from within a message handler. The persistence unit points to the default customer database on Derby/JavaDB. Both of these are NetBeans project so they should compile and deploy with out problem if you are using NetBeans (7.0 and 7.0.1).

There is a bug in the Jabberwocky container which prevents 2 or more Vorpal applications to be deployed at the same time. I’ll hopefully fix this in the near future.

CDI support for Vorpal is enabled in the usual way by adding a file call beans.xml to WEB-INF directory.

I’ll talk about conversation support in my next blog.

Follow

Get every new post delivered to your Inbox.

Join 54 other followers