Vorpal – A XMPP Application Framework

I have been using JAX-RS API (tutorial here) a lot over the past year and I really like the the API. Its simple yet flexible enough for advance usage. Clever use of annotations, flexible method signatures and works with any Servlet container. So I thought why not create a XMPP application framework for Jabberwocky. This framework, which I’ll call Vorpal, will be modelled after JAX-RS API. I’ve intentionally kept the current scope small by supporting only <message> viz. the Vorpal framework will only know how to process <message>.

Why <message>? Well, the main reason is that <message> (chat, groupchat, etc) is probably the most commonly used. It’s quite flexible in its usage and it supports both stateless and stateful use cases. As I’m experimenting with Vorpal I feel <message> offers the best model to work with.

The best way to learn something is to study some examples; so here are 4 examples showing some of the features (not exhaustive) that Vorpal currently supports.

Vorpal Tutorial

Assume we have the following incoming message

<message type=’chat’ id=’abc’ to=’romeo@montague.verona’ from=’juliet@capulet’>
   <active xmlns=’http://jabber.org/protocol/chatstates’/&gt;
   <body>You kiss by the book</hello>
</message>

Example 1

To capture this chat message write the following class

@Message
public class ProcessMessage {
   @Body(“{body}”)
   public String fromJuliet(@Parameter(“body”) String message) {
      return (message.toUpperCase());
   }
}

The @Message is a class level annotation that tells Vorpal that this class is going to handle <message>. Next write a method that is going to process the message and annotate it with @Body. @Body tells the framework that this is the method to process the message’s body viz. <body>. The parameter {body} specifies a ‘capture’ and is used to capture the content of the message’s payload and this is mapped by the @Parameter in fromJuliet() method to the formal parameter message. When you specify the capture parameter’s name in @Parameter, do not enclose the name with {}.

When we return a String after processing the message, the framework will now create the following reply back to juilet@capulet inserting the String in the body.

<message type=’chat’ id=’xyz’ from=’romeo@montague.verona’ to=’julie@capulet’>
   <body>YOU KISS BY THE BOOK</hello>
</message>

All message handler methods must be annotated, either with @Body or others, in order for them to be selected for invocation.

Example 2

Let say we only want to reserve fromJuliet() exclusively for messages from juliet@capulet; all other messages will be handle by a different method handleOthers(). We could express this in the following way

@Message @Body(“{body}”)
public class ProcessMessage {
   @From(“juliet@capulet”)
   public String fromJuliet(@Parameter(“body”) String message) {
      return (message.toUpperCase());
   }
   @From(“{anyone}@{anydomain}”)
   public String handleOthers(@Parameter(“anyone”) String name
         ,
@Parameter(“body”) String message) {
      return (“Hello ” + name + “,\n” + message);
   }
}

Firstly notice that we have move @Body to the class level; this is so that {body} is available to both fromJuliet() and handleOthers() methods. Secondly note the capture parameters in both the @From annotations. In the first annotation with juliet@capulet string; the string do not contain a pair of {}. This signifies that it is a literal and not a capture. So for fromJuliet() method to ‘fire’, the from attribute in <message> must match this literal value.

As for the second @From annotation, this is a template with a mix of capture and literal. In this case we are breaking up an incoming message’s from JID and map that to two capture parameters. Assume we have the following two JIDs mercutio@verona and holmer@springfield/moestavern sending in messages then the following diagram shows how these JIDs are captured.

{anyone} will capture mercutio and holmer and {anydomain} will capture vernoa and springfield/moestavern respective. The capture mechanism is basically a simple pattern matcher; since the @ is a literal, the incoming JID will be broken into two parts and assigned to the two respective capture parameters. If we wish to break out the resource from JID, then one could use the following template {anyone}@{anydomain}/{anyresource}.

Alert readers will notice that the JID juliet@capulet could well have matched @From(“juliet@capulet”) and @From(“{anyone}@{anydomain}”). Lets leave this discussion till after example 3.

Example 3

In the following example we want be be notified when when someone leaves so that we can send them a goodbye message; to do this, we have to examine chatstate. Setup the following class

@Message @Body(“{body}”) @From(“{anyone}@{anydomain}”)
public class ProcessMessage {
   @From(“juliet@capulet”)
   public String fromJuliet(@Parameter(“body”) String message) {
      return (message.toUpperCase());
   }
  
@From(“juliet@capulet”)
   @Namespace(uri=”http://http://jabber.org/protocol/chatstates&#8221;, tag=”gone”)
   public String goodbyeJuliet(@Parameter(“anyone”) String name) {
      return (“Parting is such sweet sorrow”);
   }
  
   @Namespace(uri=”http://http://jabber.org/protocol/chatstates&#8221;, parameter=”{chatstate}”)
   public String handleOthers(@Parameter(“anyone”) String name
         ,
@Parameter(“body”) String message)
         , @Parameter(“chatstate”) Element chatstate) {
      //Using JDK7 strings in switch
      switch (chatstate.getName()) {
         case “active”:
            return (“Hello ” + name + “,\n” + message);
         case “gone”:
            return (“Goodbye ” + name + “, see you next time”);
         case “inactive”:
            return (“Why so quiet ” + name);
         default:
      }
      return (null);
   }

}

Using @Namespace, we have access all packet extension within a packet. You may use @Namespace to specify either just the tag of the extension, or the namespace or both. In the first use of @Namespace, we specify that if juliet@capulet leaves, then we send her a sappy parting message. In the second example handleOthers(), we only specify the namespace of chatstates. There is an additional parameter field which specifies the name of the capture parameter. Now we map this to an Element type. In the body of handleOthers(), we examine the tag and give appropriate responses to active, gone and inactive. Anything else we return a null which tells Vorpal not to send any response message back. If your method is not going to return anything ever, declare the return type as void.

@Namespace may be mapped to either Element (dom4j) or a JAXB object. It can also be mapped to a String. Using dom4j/JAXB you get access into the packet extension’s document.

Soapbox – Method Resolution

So how does method resolution takes place? In example 2, you will notice that JID juliet@capulet could have matched both @From(“juliet@capulet”) and @From(“{anyone}@{anydomain}”) and therefore could have triggered both fromJuliet() and handleOthers() respectively. Before any resolution takes place, classes and methods are sorted. All classes/methods are sorted in descending order of the number of annotations. If there are 2 or more methods with the same number of annotations, then they are lexically ordered based on their method names. Based on this ordering method, the methods in ProcessMessage class in example 3 above, are ordered in the following manner

  1. goodbyeJuilet() – 2 annotations
  2. fromJuliet() – 1 annotation, lexically smaller that handleOthers()
  3. handleOthers() – 1 annotation

When a message from juliet@capulet arrives, goodbyeJuliet() will be examined first; if the message contains a chatstates extension and that tag is gone, then goodbyeJuliet() will be selected for invocation. However if the packet’s chatstate’s tag is not gone or the packet does not contain a chatstates extension, then the second method will be selected for evaluation. Since fromJuliet() only contains the @From annotation and there is a match, fromJuliet() is invoked.

Simply stated, method resolution goes from more specific (being more annotation) to general (at least 1 annotation)

Example 4

In our final example, if we receive a message to info@montague.verona, then we would like to reply with a “thank you for enquiring” message along with a vCard as part of the message extension. The reply we wish to construct looks the like following message

<message type=’chat’ id=’abc’ to=’inquisitive@verona’ from=’info@montague.verona‘>
   <body>Dear inqusitive, thank you for enquiring. Please find attached vCard</hello>
   <vCard xmlns=’vcard-temp’>
      <fn>Romeo Montague</fn>
      <nickname>Romeo</nickname>
         …
   </vCard>
</message>

where the green is our text reply and the purple is the vCard. The vCard extension can be constructed using Element or JAXB. Both of these are supported directly in Vorpal. For simplicity, I’ll use JAXB class to hold the vCard data

@XmlRootElement(name=”vCard”, namespace=”vcard-temp”)
@XmlAccessorType(XmlAccessType.PROPERTY)
public class VCard {
   private String formattedName;
   private String nickname;
   …
   @XmlElement(name=”fn”)
   public String getFormattedName() {
      return (formattedName);
   }
   …

The following method sendInfo() shows how to construct the reply message

   @Order(1)
   @To(“info@montague.verona”)
   public Set<Object> sendInfo(@Parameter(“anyone”) String name) {
      Set<Object> reply = new HashSet<Object>();
      //Add a string for the message’s body
      reply.add(“Dear ” + name + “, thank you for enquiring. Please find attached vCard”);
      //Create vCard object and populate it
      VCard vCard = new VCard();
      vCard.setFormattedName(“Romeo Montague”);
      …
      //Add this to the reply set
      reply.add(vCard);
      return (reply);
   }

Wherever you have more than one value to be packaged into the reply packet, return a Collection type (Set, List, Queue, etc.). You can now populate the collection with either dom4j Element, JAXB object or String. Elements and JAXB objects will be added as part of the packet’s extension so ensure that you have proper namespaces for these XML objects. Strings and other non Element or JAXB objects will be set as the message’s body content. If an object is not a String, then the toString() method will be invoked to obtain a String.

Note also the @Order annotation. This annotation overrides the method ordering scheme which I’ve talked about earlier. @Order are sorted in ascending order; furthermore, any method that has @Order annotation will be ‘smaller’ than any other method that do not have this annotation. So, @Order methods will be resolved first followed by non @Order methods. If 2 or more methods have the same order number, then the previously discuss sorting method will be applied. Together with sendInfo(), the method resolution order is now as follows

  1. sendInfo() – ordered 1
  2. goodbyeJuilet() – 2 annotations
  3. fromJuliet() – 1 annotation, lexically smaller that handleOthers()
  4. handleOthers() – 1 annotation

In this particular case, if we do not annotate this with @Order, then messages for info@montague.verona might be gobbled up by handleOthers() because although both sendInfo() and handleOthers() has the same number of annotation, the latter is lexically lower than the former so it will be evaluated first.

I’ll be releasing Vorpal framework soon. Meanwhile please let me know what you think of Vorapl. Till next time.

%d bloggers like this: