New Beginnings Again!

exploring-calvin-and-hobbes

 

They say third time is a or should be charm! In this blog, I’ll talk about Vorpal2, a reworked version of Vorpal.

 

Excuses, excuses, excuses

The original Vorpal grew out of an experiment and the framework was implemented on top of AbstractComponent class from Tinder. It was a really easy way for someone like me with very little knowledge of XMPP to learn about XMPP and get the framework up and running. However due to the way AbstractComponent class works, I had to repeat a same piece of code in different places. There is no single choke point for the XMPP packets coming in and leaving the framework (for that I had to modify AbstractComponent which was not very appealing); this make implementing certain features extremely difficult and in some cases impossible.

The second issue was that I made the framework dependent on a custom Glassfish container call Jabberwocky. Whilst Jabberwocky is great in that it provided a very robust base to run the framework, integrated extremely well with Glassfish asadmin command and provided all sort of statistics on a Vorpal application, the down side was that it effectively ties Vorpal to Glassfish. Also Jabberwocky is build with Maven. So whenever there is a new release of Glassfish I have to figure out which dependencies have change, etc.

Finally testing the framework engine’s becomes difficult because of the 2 dependencies above.

So I’ve decided to reimplement Vorpal; the good news for me is that most of the framework is fine. I just have to rewrite how the Vorpal is loaded. I’ve removed Vorpal’s dependence on AbstractComponent and Jabberwocky; the startup is now done in ServletContainerInitializer. Its lightweight and in theory should run on any JavaEE 6 Web Profile compliant container. Glassfish will still be the best supported platform simply because this is where I develop and test Vorpal.

For the rest of this blog, I’ll talk about migrating from Vorpal to Vorpal2, packet filters which is a new feature available only in Vorpal2.

 

The Great Migration

No code change (except for ComponentManager)

I’m happy to say that there are no code changes going from Vorpal to Vorpal2 but if your application uses ComponentManager then you’ll have to rewrite that portion of it.  ComponentManager is an artefact of AbstractComponent. In the past I’ve allowed you to inject ComponentManager into your message handlers. This is no longer true; all the functions in ComponentManager can now be found in ComponentContext.

New JARs

The new Vorpal2 bundle can be found here (vorpal2). As before, the framework is split into 2 JARs

  • vorpal2.jar – the framework
  • jabberwocky_support2.jar – framework supporting files

If you have been using Vorpal, you’ll need to uninstall them. Delete jabberwocky.jar and jabberwocky_support.jar from $GLASSFISH_HOME/glassfish/modules directory.

As before, vorpal2.jar must be bundled with your application in WEB-INF/lib.

There are 2 ways you can install jabberwocky_support2.jar. You can either bundle that with your application in WEB-INF/lib or install it at your application’s CLASSPATH. For Glassfish this will be the $GLASSFISH_HOME/glassfish/modules directory.

At the writing of this blog, I’ve not fully migrated the NetBeans plugin to support Vorpal2 (hence the missing update directory from the download bundle). Hopefully I’ll have the plugins done by the next release. In the mean time, you’ll have to manage the build and package yourself. In NetBeans, add the the JARs from the bundle to your project’s library and remove the previous version of the libraries. Also remember that these JARs, or at the minimum vorpal2.jar, must be in WEB-INF/lib of the final WAR file.

Deployment descriptor

Vorpal uses a deployment file to connect to an XMPP server. The file is called xep-0114.xml. This file is found in WEB-INF directory. If you’re developing Vorpal applications with the NetBeans plugin, the template of this deployment file is in your project directory and its called xep-0114.txmlt.

The deployment file contains the following pieces of information

  • The XMPP domain, port number to connect to
  • The subdomain name we wish to use
  • The class name of the component to start – this is very specific to the frame
  • An option to specify the shared secret when connecting to the XMPP domain
  • An optional list of properties to be passed to the application

The file looks like the following

<subdomain domain=”batcomputer” port=”5275” name=”customer”>
   <component-class name=”com.kenai.jabberwock.framework.core.JabberwockyComponent”/>
   <create-subdomain shared-secret=”AlwaysRight” create=”true” cleanup=”true”/>
   <properties>
      <property name=”vorpal.component.name” value=”Customer service”/>
      <property name=”vorpal.component.description” value=”Customer service”/>
   </properties>
</subdomain>

What you need to do is the following

  • Change the component-class name from com.kenai.jabberwock.framework.core.Jabberwocky to com.kenai.jabberwocky.framework.core.XMPPComponentServer
  • Always specify the shared secret with create-domain. The create and cleanup attributes must be present but their values are ignored.

So the new deployment file xep-0114.xml looks like below

<subdomain domain=”batcomputer” port=”5275” name=”customer”>
   <component-class  name=”com.kenai.jabberwock.framework.core.XMPPComponentServer”/>
   <create-subdomain shared-secret=”AlwaysRight” create=”true” cleanup=”true”/>
   <properties>
      <property name=”vorpal.component.name” value=”Customer service”/>
      <property name=”vorpal.component.description” value=”Customer service”/>
   </properties>
</subdomain>

Recompile your application after you’ve made these changes.

New Features

Packet filtering

Packet filtering is one of the feature that I wanted to implement into Vorpal. But I could not do this easily under the old code base until now.

Packet filter are filters that are applied to incoming and outgoing XMPP packets. To implement a packet filter, first annotate your class with @PacketFilter and either implement RequestFilter or ResponseFilter (or both!). The following is an example of a request filter that filters out packets that are not send from the local domain

@PacketFilter
public class LocalEntityOnlyFilter implements RequestFilter {
    @Inject @Named(PredefinedBindings.DOMAIN) String domain;
    //Inspired by AbstractComponent.sentByLocalEntity(Packet packet)
    @Override
    public Packet incoming(final Packet in
          , ComponentContext context)
{
        final JID jid = in.getFrom();
        if (null != jid)
            return (in);
        String pktDomain = jid.getDomain();
        if (pktDomain.equals(domain)
               || pktDomain.endsWith("." + domain))
            return (in);
        return (null);
    }
}

Firstly we see that our filter is annotated with @PacketFilter and implements RequestFilter. So what this mean is that this filter will only monitor incoming packets viz. packets sent to the Vorpal application.

Filters are managed by CDI. so you can injected values into it. In this case we are injecting the domain name that the subcomponent is connected to. Filters are statesless so you should not use any CDI scopes on it.

In the filter, you can do anything to the packet; you can add stanzas, remove stanzas and change certain values in the packet. When you return a packet, this can be the original packet from the parameter or a totally new packet, Vorpal will take the packet and proceed with finding an appropriate handler. If you return a null, then Vorpal will discard the packet.

A ResponseFilter behaves the same way as a RequestFilter. When you implement ResponseFilter, you have to override the outgoing(Packet, ComponentContext) method which returns a Packet as well. If you return a null, then Vorpal will not send the packet.

If you have more that one request or response filter, Vorpal does not impose any ordering on them. Also if one of the filter rejects a packet, then that packet will not be processed by other filters. For example if you have 3 request filters; the first of these 3 filters rejects the packet, then that packet will not be passed to the other 2 to be processed.

Ordering sent packets

One side effect of this new version is you can now impose some order on how the packets are send out by returning a List in your message handler. The following code snippet illustrates this

@Body(“{body}”)
private List<ResponseContext> handle(@Named(“body”) String body) {
   …
}

handle() returns a List; so Vorpal will send out the packet according to the order of the packets in the list.

Let me know what you think of Vorpal2. If you have any issues, please post them to the forum. The new bundle can be found here.

Using Server Sent Events with Vorpal

bridge

I came across the following 2 blogs (here and here) on a CDI framework for server sent event (SSE) several months ago. I downloaded the source then tried it with Vorpal running on Glassfish 3.1.x. The result was a really long exception stack.

4 months later.

After publishing part 4 of on conversations, I decided to see if I have any luck with the SSE framework. I wasn’t really expecting it to work. Well it did. What is even more surprising is that it worked with Vorpal. 

What I’m going to do in this blog is explain how the SSE framework works and more importantly how you can use it with Vorpal. Its really easy.

Setup the push event stream

Firstly create a notification handler. The notification handler will maintain the connection between the browser and the web application. The following is a simple notification handler

@ServerSentEvents(“/notifications”)

public class NotificationHandler extends ServerSentEventHandler {

   public void sendMessage(String msg) {

      try {

         connection.sendMessage(msg);

      } catch (IOException ex) {

      }

   }

A few points to note: the notification handler must extend ServerSentEventHandler; this class is from the framework. You also have to annotate the class with @ServerSentEvents and specify the URL of the event stream. In this case it is notifications. More on this later.

Now you need to write a HTML/JSP/JSF/whatever page to invoke this event stream. Here is a snippet of the HTML page along with a snippet of Javascript that loads loads the event stream

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>XMPP with SSE</title>
        <script src="resources/app.js"></script>
    </h:head>
    <h:body>
        <h1>Using Vorpal with SSE</h1>       
    </h:body>
</html>

The Javascript that creates the event stream is in app.js. Here is the code

var url = ‘http://’ + document.location.host

      + “/xmppsse/notifications”;

eventSource = new EventSource(url);

eventSource.onmessage = {

   document.body.innerHTML += msg + ’<br>’;

};

The important thing here is to get the event stream URL right. If our example, this is made up of the application context of our web application (in blue) and the value that you’ve specified in @ServerSentEvent annotation (in green).

 

Pushing data to the browser

We now hook the event handler to a Vorpal application. I did a screencast on developing a customer query service which allows you to use a Jabber client to query a database, which you can find here and the source here.

Lets extend this so that whenever someone performs a query, the result is also pushed to the browser using SSE.

@Message

public class MessageHandler {

   @EJB CustomerManager mgr;

   @Inject @ServerSentEventContext(“/notifications”)

        ServerSentEventHandlerContext<NotificationHandler> ctx;

   @Body(“{id}”)

   private String handle(@Named(“id”) int id) {

      //Assume we find every record and no NPE

      String record = mgr.find(id).toString();

      //Loop through all handlers

      for (NotificationHandler nh: ctx.getHandlers())

         try {

            nh.sendMessage(record);

         } catch (Exception ex) {

            nh.close();

         }

      return (record);

   }

After we got the record from CustomerManager, which uses JPA, we loop through all the registered handlers for an event stream channel and pushes the record to them. You have to get the correct handler to push to.

In our example, we are pushing to all browser registered on notifications. So we inject an instance of ServerSentEventHandlerContext with qualifying it with @ServerSentEventContext(“notifications”).

Example code

You can download the example source from here. I’ve bundled the SSE library into the download which is a NetBeans project.

Use the following steps to setup, deploy and run the example

  1. Configure an external component on your Jabber server. The example uses customer for subdomain and AlwaysRight for shared secret (Caps!). You can always change these settings. I use Openfire for the Jabber server
  2. Make sure that you’ve installed your Glassfish with Jabberwocky container.
  3. Deploy the project to Glassfish either from NetBeans or using asadmin command. It’s probably easier if you deploy if from NetBeans
  4. Open your browser and point to http://yourserver:port/xmppsse. I’ve tested with Firefox and Chrome, latest versions.
  5. Open your Jabber client, open a chat with query@customer.your_jabber_server. Send 1
  6. You should now see your reply display in your Jabber client and in your browser.

The source code for the SSE can be found here if you wish to look at it.

Part of the code (the app.js) is taken from Bhakti Mehta’s blog. Big thanks.

Till next time.

Listing Supported PubSub Features

When you perform a ServiceDiscoveryManager.getItems() on a pubsub JID (see Making Sense of Services), the server returns 2 types of information:

  1. the identity (<identity>) of the JID as defined in the Service Discovery Identities. You use the information here to determine what exactly is the JID. If you are looking for a pubsub service, then the category should contain pubsub value.
  2. the feature (<feature>) that is supported by the identity. In the case of a pubsub, these can be features like ability to create/delete nodes, persist items, etc. A list of the pubsub features can be found here.

This is typically how the packet will look like

<iq id=”SOKMA-5″ to=”fred@localhost/Smack” from=”pubsub.localhost” type=”result”>
<query xmlns=”http://jabber.org/protocol/disco#info”&gt;
<identity category=”pubsub” name=”Publish-Subscribe service” type=”service”/>
<feature var=”http://jabber.org/protocol/pubsub”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#collections”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#config-node”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#create-and-configure”/&gt;
<feature var=”http://jabber.org/protocol/pubsub#create-nodes”/&gt;


</query>
</iq>

Although Smack allows you to retrieve the list of identities by the DiscoveryInfo.getIdentities() method, there seems to be no provision for retrieving a list of supported features associated with an identity (see thread). While there is a getFeatures() method, this however is not public. The only way seem to be explicitly asking if a feature is supported (DiscoverInfo.containsFeature(String)); for example if you want to check if a pubsub service supports user created nodes, you do the following

DiscoverInfo info = mgr.discoverInfo(“pubsub_jid@xmpp_server.org”);
if (info.containsFeature(“http://jabber.org/protocol/pubsub#create-nodes&#8221;)) {
//do something

So to get a list of the features here is a utility method

public static List<DiscoverInfo.Feature> extractFeature(String s) throws Exception {
   List<DiscoverInfo.Feature> list = new LinkedList<DiscoverInfo.Feature>();
   QName var = new QName(“var”);
   ByteArrayInputStream bis = new ByteArrayInputStream(s.getBytes());
   XMLEventReader xer = XMLInputFactory.newFactory().createXMLEventReader(bis);
   out:
   while (xer.hasNext()) {
      XMLEvent evt = xer.nextEvent();
      switch (evt.getEventType()) {
  
      case XMLStreamConstants.START_ELEMENT:
            StartElement st = evt.asStartElement();
            if (“feature”.equals(st.getName().getLocalPart()))
               list.add(new DiscoverInfo.Feature(
            st.getAttributeByName(var).getValue()));
            break;
         case XMLStreamConstants.END_DOCUMENT:
            break out;
         default:
      } //switch
   } // while
   xer.close();
   bis.close();
   return (list);
}

and the following code snippet shows how to use this utility method

DiscoverInfo info = mgr.discoverInfo(“pubsub_jid@xmpp_server.org”);
List<DiscoverInfo.Feature> featureList = extractFeature(info.getChildElementXML());

%d bloggers like this: