BEEP


Blocks Extensible Exchange Protocol (BEEP, formerly known as BXXP) is a relatively new protocol, and is rapidly gaining industry attention. It would be an understatement to think of BEEP as a mere application protocol. It is a protocol that can act as a framework over which other protocols can be built upon.

RFC 3080 discusses the BEEP Core, and RFC 3081 explains mapping the Core on TCP.

BEEP Jargon

Before getting into the details of the BEEP protocol, let us first define some important terms that you will come across:

  • Peer Any BEEP-enabled endpoint in a network is called a peer. Typical to any peer-to-peer protocol, BEEP makes no assumptions about the endpoint. A peer can act as a client in some cases, and as a server in others.

  • Initiator/listener It does not make sense to use the terms "client" or "server" in a peer-to-peer environment, in which such roles change dynamically. Yet at a per-request level, there is always an entity that initiates a communication, and an entity that listens to initiated communications. BEEP always refers to entities as initiators and listeners. The initiator performs a client role, and the listener performs a server role. Of course, these roles are not static. A peer that is an initiator for one request could be a listener to the following request.

  • Session A session denotes a single connection between two peers. A session is mapped to the underlying transport service that the BEEP framework is built upon. In the case of TCP, a BEEP session is mapped to a single TCP connection (see RFC 3081).

  • Channels All data exchanges between communicating peers occurs through channels. A session can be therefore divided into multiple channels. Because all channels use the same session and hence the same connection, channels are created by multiplexing a single connection.

  • Profile A profile defines the syntax of valid messages that can be exchanged within a channel. Each channel is associated with a profile.

How BEEP Works

As with most network protocols, BEEP communication starts with a listener, listening for connections. The initiator initiates a session (connection) with the listener.

All data exchanges can only take place through BEEP channels. However, during the initial communication between the initiator and the listener, each one has no idea about the channels and profiles supported by the other. For this reason, a special channel called the management channel (also called channel zero) exists. The management channel supports a well-known profile called the channel management profile. Channel zero helps the peers advertise to each other about the various profiles that they support. It also helps the peers create new channels based on one of these mutually supported profiles.

Once new channels are created, the entities can communicate with each other using messages adhering to the profile that they support. Multiple channels can be in use simultaneously.

There are two types of channels:

  • Initial tuning channels Present only during the initialization process. Their main aim is to provide the initialization services before any actual communication starts. These channels become inactive after the initialization phase. Only one tuning channel can exist in a session at a time. For instance, there could be an initial tuning channel that authenticates the initiator and listener to each other.

  • Continuous channels These carry the actual data being exchanged. They are typically activated after the initialization phase is over. A session can have multiple simultaneous continuous channels.

Once the data exchange between the initiator and the listener is complete, channel zero is again used to close all the open channels.

Using the BEEP Java Binding

BEEP has been implemented in various programming languages like Java, C, TCL, Python, Ruby etc. In order to illustrate how BEEP works, let us use the BEEP Java Binding. The BEEP Java binding is available from beepcore.org. It is the reference implementation of RFC 3080 and 3081.

We will develop an example of a simple message exchange using the BEEP protocol. You'll first need to install the BEEP Java binding, which can be found at www.beepcore.org.

Note that to successfully install the binding you also need

  • Xerces, or any other XML parser that supports the javax.xml.parsers, org.w3c.dom, and org.sax.xml interfaces.

  • The optional JSSE library from Sun Microsystems. However, if you are using JDK 1.4 or above, you won't need this. The library has been integrated along with the core Java classes from this release.

The HelloListener Class

The HelloListener class is a simple BEEP listening peer. It simply listens for any incoming BEEP session. Once a session and a channel are established, it receives whatever message the initiator sends. It also sends back a reply for this message and exits. The HelloListener class can be seen in Listing 7.1.

Listing 7.1 The HelloListener Class
 import org.beepcore.beep.core.*; import org.beepcore.beep.profile.Profile; import org.beepcore.beep.profile.ProfileConfiguration; import org.beepcore.beep.profile.echo.EchoProfile; import org.beepcore.beep.transport.tcp.AutomatedTCPSessionCreator; import org.beepcore.beep.transport.tcp.TCPSession; import java.io.BufferedReader; import java.io.InputStreamReader; /**  * This is a BEEP Peer which listens for an incoming  * request. The port and profile string are defined  * by the constants. The peer also send back a response to the  * initiator of the message that it receives.  */ public class HelloListener{   // Port number that is used   public  static final int    PORT           = 8888;   // This string is a URI that represents the profile.   public  static final String PROFILE_STRING = "http://testdomain.com/test";   // private variable to check if the application can exit   private static boolean      exit           = false;   /**    * The main method creates an instance of the HelloListener.    * The listener is made to listen for incoming messages.    */   public static void main(String args[]) throws Exception{     // Create a new HelloListener object and listen     // for any initiator     new HelloListener().listenForInitiator();   }   /**    * The method listens for a TCP Session to be initiated by    * the initiator. Once a session is obtained the method exits    * after a sleep interval.    *    */   public void listenForInitiator() {     log("Starting to Listen on port "+PORT);     // create a new ProfileRegistry     ProfileRegistry registry = new ProfileRegistry();     // Add a StartChannelListener to the registry.     // This listener will contain logic to manage     // starting and stopping of channels.     // In this case we use the HelloProfile to     // get an instance of the HelloChannelListener.     try{       registry.addStartChannelListener(PROFILE_STRING,         new HelloProfile().init(PROFILE_STRING,null),null);     } catch(BEEPException beepException){       // This means that creating a profile failed       // The listener should be exited       exit = true;     }     // continue till the exit flag is true.     while(!exit){       try{         // Get a TCPSession.         // This is a blocking call.         TCPSession session =           AutomatedTCPSessionCreator.listen(PORT,registry);         // sleep for a small delay.         // This ensures consistency of output.         Thread.sleep(2000);       } catch(BEEPException beepException){         // This means that the application is not         // able to listen properly         // Try to abort.         log("Unable to listen Message is : "+             beepException.getMessage());         exit = true;       } catch(InterruptedException excp){         // No OP       }     }     log("Exiting ...");   }   /**    *  A simple Beep profile  which implements the    *  <code>Profile</code> interface.    *    */   class HelloProfile implements Profile{     /**       * Initializes the profile and returns an appropriate      * <code>StartChannelListener</code>.      *      */     public StartChannelListener init(String uri, ProfileConfiguration config)                               throws BEEPException{        return new HelloChannelListener();     }   }     /**       * A Listener implementing the <code>StartChannelListener</code>       * interface.It contains logic for managing the start and stop       * events in a given channel.      */     class HelloChannelListener implements StartChannelListener{       /**        * Callback method that determines if a profile can be advertised.        * If there are any prerequisites for a profile, they must be checked        * here.Only        *        */        public boolean advertiseProfile(Session session)                          throws BEEPException{         return true;       }       /**         * Called when a new channel is started.        *        */       public void startChannel(Channel channel,                            java.lang.String encoding,                            java.lang.String data)                            throws StartChannelException{         //create a new data listener for this channel.           channel.setDataListener(new HelloDataListener());       }       /**        * Called before a channel is closed.        */       public void closeChannel(Channel channel)                       throws CloseChannelException{         // reset the data listener         channel.setDataListener(null);         // reset the application context         channel.setAppData(null);       }     }      /**      * A Message listener which gets notified whenever      * any message is received. It implements the      * <code>MessageListener</code> interface.      */     class HelloDataListener implements MessageListener{       /**        * Receives a BEEP message.        */       public void receiveMSG(Message message) throws BEEPError,                                           AbortChannelException{         try{           // Get a data stream from the message.           DataStream stream = message.getDataStream();           // Create a buffered reader for reading the data.           BufferedReader reader =             new BufferedReader(new InputStreamReader               (stream.getInputStream()));           // Print the recieved message to console.           log("Message from initiator => "+reader.readLine());           // Close the reader           reader.close();           // Send a reply to the initiator of the message           log("Sending a reply to initiator ...");           message.sendRPY(new StringDataStream("Hello Initiator !"));           // exit the application           exit = true;         } catch(java.io.IOException excp ){           log("Message from Exception : "+excp.getMessage());           throw new AbortChannelException(excp.getMessage());         } catch(BEEPException beepExcp){           log("Message from Exception : "+beepExcp.getMessage());           throw new AbortChannelException(beepExcp.getMessage());         }       }   }   // private method to log messages to console   private static void log(String message){     System.out.println(message);   } } 

The AutomatedTCPSessionCreator is used by HelloListener to create a TCP-based BEEP session. This is done by the static listen(port,registry) method. This is a blocking call, and will do so until a TCPSession is created. The registry required by this method is ProfileRegistry, a class that contains a list of listeners (implementations of the StartChannelListener interface) that need to be notified when a new channel is started in this session.

We have also created a simple profile called HelloProfile. This profile also has its own StartChannelListener called HelloChannelListener. Obviously, even before we start to listen for a session, we need to add the HelloChannelListener to the ProfileRegistry so that it is notified of all channel management events.

Although the HelloChannelListener is aware of all channel management events (the starting of a new channel, the closing of an existing channel, and so on), it is unaware of any incoming messages through this channel. For this purpose, we need a MessageListener.

We have a HelloDataListener, an implementation of MessageListener that we use in this example. This listener's receiveMSG method is invoked on the receipt of any message. Whenever a new channel is started, the HelloChannelListener sets up a new MessageListener to receive all messages sent through the channel. Invoking the setDataListener method of the Channel object by passing a new instance of the HelloDataListener as an input achieves this.

The HelloDataListener listens to all incoming messages and prints the content of each message to the console. It also sends back a reply to the initiator of the message. This is achieved by the sendRPY method of the Message object.

The HelloInitiator Class

The HelloInitiator represents an initiating BEEP peer. It tries to open a session with the HelloListener. The HelloInitiator is listed in Listing 7.2.

Listing 7.2 The HelloInitiator Class
 import org.beepcore.beep.core.*; import org.beepcore.beep.lib.NullReplyListener; import org.beepcore.beep.transport.tcp.AutomatedTCPSessionCreator; import org.beepcore.beep.transport.tcp.TCPSession; import java.io.BufferedReader; import java.io.InputStreamReader; /**  * This is a BEEP Peer which initiates a session with the listener peer.  * The host name of the listener peer is contained in the HOST variable.  * Currently it points to the same host as the initiator. This can be  * changed to point to any host.  *  */ public class HelloInitiator{   /**    * Host name of the listener    */   public static final String HOST           = "localhost";   /**    * The main method creates an instance of the    * <code>HelloListener</code>    * The initiator is made to connect to the listener and send a    * message. It exits after a small sleep interval.    *    */   public static void main(String args[]){     try{       // initiate a connection to the listener running on HOST       TCPSession session =         AutomatedTCPSessionCreator.initiate(HOST,HelloListener.PORT,                                                    new ProfileRegistry());       // Start a new Channel with a profile as by       // the PROFILE_STRING uri.       Channel channel = session.startChannel(HelloListener.PROFILE_STRING);       // Create  a new StringDataStream           DataStream messageData = new StringDataStream("Hello Listener !!");       // send the message           channel.sendMSG(messageData,new HelloReplyListener());       // sleep for a small delay and exit.       Thread.sleep(2000);     } catch(BEEPException beepException){       log("Unable to send a successful Message. Reason is : "+           beepException.getMessage());     } catch(InterruptedException excp){       // NO OP     }   }   /**    * A listener implementing the <code>ReplyListener</code> interface.    * A reply listener can be used per message sent.    *    */   static class HelloReplyListener implements ReplyListener{     /**      * Called when a reply is received .      *      */     public void receiveRPY(Message message) {       try{         // Get a data stream from the message.         DataStream stream = message.getDataStream();         // Create a buffered reader for reading the data.         BufferedReader reader =           new BufferedReader(new InputStreamReader             (stream.getInputStream()));         // Print the recieved message to console.         log("Message from listener => "+reader.readLine());         // Close the reader         reader.close();           ((FrameDataStream)message.getDataStream()).close();       } catch(Exception excp ){         log("Message from Exception : "+excp.getMessage());       }     }     /**      * Called when the underlying a reply of type ERR is received.      * Not used in this example.      *      */     public void receiveERR(Message message) {         ((FrameDataStream)message.getDataStream()).close();     }     /**      * Called when the underlying a reply of type ANS is received.      * Not used in this example.      *      */     public void receiveANS(Message message) {         ((FrameDataStream)message.getDataStream()).close();     }     /**      * Called when the underlying a reply of type NUL is received.      * Not used in this example.      *      */     public void receiveNUL(Message message){     }   }   // private method to log messages to console   private static void log(String message){     System.out.println(message);   } } 

The HelloInitiator also uses the AutomatedTCPSessionCreator class to initiate a session. This is achieved by using the initiate method. Once a session is initiated, it starts a new channel, with the given URI representing the profile. This is denoted by the constant PROFILE_STRING. It is important for both the HelloListener and the HelloInitiator to use the same URI, which serves as an identifier to the profile.

Once a channel is set up, the HelloInitiator sends a simple string message to the listening peer. This is done by the sendMSG method of the channel object.

Now every message that is sent to a listening peer can have a corresponding reply. The sender is free to delegate a listener for receiving the reply. All such listeners have to implement the ReplyListener interface. The HelloInitiator uses the HelloReplyListener as a listener to the reply it receives from the HelloListener.

APEX

Application Exchange (APEX) is an application protocol defined on top of BEEP. In simple terms, it is a BEEP profile. It provides services such as an access service that can be used to manage access information at endpoints, a presence management service, a report service, and so on.

APEX uses a mesh of interconnecting relays to transfer data from one APEX endpoint to another. All communications are sent to the mesh, which is responsible for sending the messages to the appropriate endpoint, regardless of the location to which it is connected. At the time of this writing, no implementation of APEX exists.

The Future of BEEP

BEEP holds a lot of promise to act as a base protocol upon which to build the application protocols of the future. A lot of work is already going in this direction. Many interesting BEEP profiles are currently being defined, including the following:

  • Intrusion Detection Exchange Profile (IDXP)

  • Tunnel Profile

  • Common Presence and Instant Messaging Profile (CPIM)

Many applications have started looking seriously into BEEP. For instance, the JXTA reference binding uses BEEP as one of the supported communication protocols.

However, BEEP may not be appropriate for all cases. For example:

  • If an application uses an already existing and widely deployed protocol, it would make little sense in rewriting it to use BEEP.

  • BEEP does not make sense when the underlying communication protocol is multicast- or broadcast-based.

  • If the amount of data exchanged is very small, BEEP becomes an overhead.



JavaT P2P Unleashed
JavaT P2P Unleashed
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 209

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net