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

Advertisements

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

%d bloggers like this: