Chapter 17. Writing Plugin Code
Prev |  | Next

# Chapter 17. Writing Plugin Code

Artur Hefczyc <artur.hefczyc@tigase.net> v2.0, June 2014: Reformatted for AsciiDoc. :toc: :numbered: :website: http://tigase.net/ :Date: 2010-04-06 21:22

Previous guide describes a basic idea behind the XMPP stanza processing in the session manager. As it was already point out the processing takes place in 4 steps. A different kind of plugin is responsible for each step of processing:

1. XMPPPreprocessorIfc - is the interface for packets pre-processing plugins.
2. XMPPProcessorIfc - is the interface for packets processing plugins.
3. XMPPPostprocessorIfc - is the interface for packets post-processing plugins.
4. XMPPPacketFilterIfc - is the interface for processing results filtering.

If you look inside any of these interfaces you find only a single method. This is it. This is where all the packet processing takes place. All of them take a similar set of parameters and below is a description for all of them:

- Packet packet - packet is which being processed. This parameter may never be null. Even though this is not immutable object it mustn’t be altered. None of it’s fields or attributes can be changed during processing.
- XMPPResourceConnection session - user session which keeps all the user session data and also gives an access to the user’s repository data. It allows for storing information in a permanent storage or in memory only during the live of the online session. This parameter can be null if there is no online user session at the time of the packet processing.
- NonAuthUserRepository repo - this is a user data storage which is normally used when the user session (parameter above) is null. This is repository allows for a very restricted access only. It allows for storing some user private data (doesn’t allow overwriting existing data) like messages for offline users and it also allows for reading user public data like VCard.
- Queue<Packet> results - this a collection with packets which have been generated as input packet processing results. Regardless a response to a user request is sent or the packet is forwarded to it’s destination it is always required that a copy of the input packet is created and stored in the results queue.
- Map<String, Object> settings - this map keeps plugin specific settings loaded from the Tigase server configuration. In most cases it is unused, however if the plugin needs to access an external database that this is a way to pass database connection string to the plugin.

After a closer look in some of the interfaces you can see that they extend another interface: XMPPImplIfc which provides a basic meta information about the plugin implementation. Please refer to JavaDoc documentation for all details.

For purpose of this guide we are implementing a simple plugin handling all <message/> packets, that is forwarding packets to the destination address. Incoming packets are forwarded to the user connection and outgoing packets are forwarded to the external destination address. This message plugin is actually implemented already and it is available in our Git repository. The code has some comments inside already but this guide goes deeper into the implementation details.

First of all you have to chose what kind of plugin you want to implement. If this is going to be a packet processor you have to implement XMPPProcessorIfc interface, if this is going to be pre-processor then you have to implement XMPPPreprocessorIfc interface. Of course your implementation can implement more than one interface, even all. It depends on your use case and needs. There are also two abstract helper classes from which you should use one as a base for all you plugins XMPPProcessor or AnnotatedXMPPProcessor for annotation support.

## Using annotation support

The class declaration should look like this (assuming you implement just packet processor):

    public class Message extends AnnotatedXMPPProcessor
       implements XMPPProcessorIfc

The first thing to create is the plugin ID. This is a unique string which you put in the configuration file to tell the server to load and use the plugin. In most cases you can use XMLNS if the plugin wants packets with elements with a very specific name space. Of course there is no guarantee there is no other packet for this specific XML element too. As I want to process all messages and I don’t want to spend whole day on thinking about a cool ID let’s say our ID is: 'message'.

A plugin informs about it’s using static ID field and @Id annotation placed on class:

    @Id(ID)
    public class Message extends AnnotatedXMPPProcessor
       implements XMPPProcessorIfc {
      protected static final String ID = "message";
    }

As I mentioned before such a plugin receives only this kind of packets for processing which it is interested in. My plugin is interested only in packets with <message/> elements and only if they are in "jabber:client" namespace. To indicate all supported elements and namespaces we have to add 2 more annotations:

    @Id(ID)
    @Handles({
      @Handle(path={ "message" },xmlns="jabber:client")
    })
    public class Message extends AnnotatedXMPPProcessor
       implements XMPPProcessorIfc {
      private static final String ID = "message";
    }

Prev |  | Next
Chapter 16. How Packets are Processed by the SM and Plugins | Home | Using older non-annotation based implementation