<?xml version="1.0" encoding="UTF-8"?>
<chapter id="writing">
    <title>Writing protocols</title>

    <para>
        This chapter discusses how to write custom protocols
    </para>


    <section>
        <title>Writing user defined headers</title>
        <para>
            Headers are mainly used by protocols, to ship additional information around with a message, without
            having to place it into the payload buffer, which is often occupied by the application already. However,
            headers can also be used by an application, e.g. to add information to a message, without having to
            squeeze it into the payload buffer.
        </para>
        <para>
            A header has to extend org.jgroups.Header, have an empty public constructor and implement the
            <classname>Streamable</classname> interface (writeTo() and readFrom() methods).
        </para>
        <para>
            A header should also override size(), which returns the total number of bytes taken up in the
            output stream when an instance is marshalled using Streamable. Streamable is an interface for
            efficient marshalling with methods
        </para>
        <programlisting language="Java">
public interface Streamable {

    /** Write the entire state of the current object (including superclasses)
        to outstream. Note that the output stream must not be closed */
    void writeTo(DataOutput out) throws IOException;

    /** Read the state of the current object (including superclasses) from
        instream. Note that the input stream must not be closed */
    void readFrom(DataInput in) throws IOException,
                                       IllegalAccessException,
                                       InstantiationException;
}
        </programlisting>

        <para>
            Method writeTo() needs to write all relevant instance variables to the output stream and readFrom() needs
            to read them back in.
        </para>
        <para>
            It is important that size() returns the correct number of bytes, because some components (such a message
            bundling in the transport) depend on this, as they need to measure the exact number of bytes before sending
            a message. If size() returns fewer bytes than what will actually be written to the stream, then it is
            possible that (if we use UDP with a 65535 bytes maximum) the datagram packet is dropped by UDP !
        </para>
        <para>
            The final requirement is to add the newly created header class to jg-magic-map.xml (in the ./conf directory),
            or - if this is not a JGroups internal protocol - to add the class to ClassConfigurator. This can be done
            with method
        </para>
        <programlisting language="Java">ClassConfigurator.getInstance().put(1899, MyHeader.class)</programlisting>.

        <para>
            The code below shows how an application defines a custom header, MyHeader, and uses it to attach additional
            information to message sent (to itself):
        </para>
        <programlisting language="Java">
public class bla {

    public static void main(String[] args) throws Exception {
        JChannel ch=new JChannel();
        ch.connect("demo");
        ch.setReceiver(new ReceiverAdapter() {
            public void receive(Message msg) {
                MyHeader hdr=(MyHeader)msg.getHeader("x");
                System.out.println("-- received " + msg +
                                   ", header is " + hdr);
            }
        });

        ClassConfigurator.getInstance().add((short)1900, MyHeader.class);

        int cnt=1;
        for(int i=0; i &lt; 5; i++) {
            Message msg=new Message();
            msg.putHeader((short)1900, new MyHeader(cnt++));
            ch.send(msg);
        }
        ch.close();
    }


    public static class MyHeader extends Header implements Streamable {
        int counter=0;

        public MyHeader() {
        }

        private MyHeader(int counter) {
            this.counter=counter;
        }

        public String toString() {
            return "counter=" + counter;
        }

        public int size() {
            return Global.INT_SIZE;
        }

       public void writeTo(DataOutputStream out) throws IOException {
            out.writeInt(counter);
       }

       public void readFrom(DataInputStream in) throws IOException,
                                                       IllegalAccessException,
                                                       InstantiationException {
            counter=in.readInt();
       }
    }
}
        </programlisting>

        <para>
            The MyHeader class has an empty public constructor and implements the writeExternal() and readExternal()
            methods with no-op implementations.
        </para>
        <para>
            The state is represented as an integer counter. Method size() returns 4 bytes (Global.INT_SIZE),
            which is the number of bytes written by writeTo() and read by readFrom().
        </para>
        <para>
            Before sending messages with instances of MyHeader attached, the program registers the MyHeader class with
            the ClassConfigurator. The example uses a magic number of 1900, but any number greater than 1024 can
            be used. If the magic number was already taken, an IllegalAccessException would be thrown.
        </para>
        <para>
            The final part is adding an instance of MyHeader to a message using Message.putHeader(). The first argument
            is a name which has to be unique across all headers for a given message. Usually, protocols use the protocol
            name (e.g. "UDP", "NAKACK"), so these names should not be used by an application. The second argument is
            an instance of the header.
        </para>
        <para>
            Getting a header is done through Message.getHeader() which takes the name as argument. This name of course
            has to be the same as the one used in putHeader().
        </para>

    </section>
</chapter>