Overview
MDL
Omnic
API
Support
Discussion
CVS
Download
Contact
Message Definition Language

Message definition language (mdl) is a declarative language used to describe message-based protocols. For each message type in the protocol, a corresponding message block specifies the datatype and name of each message field. The canonical form of an mdl file is shown below:

global-import;

message name1 {
	local-import;
	type var;
	...
}

message name2 {
	local-import;
	type var;
	...
}

.
.
.
    

Figure 1. Canonical Form

The Omni compiler, omnic, generates several files for each message block in the mdl file. The first is a message base class. For each data field in the message, the base class will contain private data members and public get/set access methods. Additionally, omnic generates a toString() method for the message. This base class source code is ready to compile without any modification by the developer. In fact, when you use the -c option, the omni compiler will compile the base classes for you.

Next, for each message block omnic generates a skeletal leaf class consisting of nothing but an empty run() method. The developer must modify the run() method to perform useful and appropriate message handling. When a message is received on an OmniSocket, the socket creates and runs a message object of the corresponding type. Since the run() method is invoked in a separate thread, you can do just about anything in the message handler.

As a convenience to the developer, omnic also generates a skeletal event handler class. The developer must modify the event handler class file by implementing useful event methods. The four events supported by the OmniSockets framework are: socket connection, socket disconnect, message received, unrecognizable message received. It is not required to implement all four, you may choose to implement some and ignore others (i.e. use the default event method implementations in the OmniEventHandler superclass). If you really wish to do nothing when an event type occurs, you should modify its event method to just return.

The following example illustrates just about every feature of mdl. The FooBar protocol contains just two message types, Foo and Bar.

# FooBar messages

import java.net.*;

message Foo {
	import	java.util.BitSet;
	int	seq;
	BitSet	bitmap;
	URL	url;
}	

message Bar {
	boolean	flag;
	InetAddress inaddr;
}	
    

Figure 2. FooBar.mdl

When compiled with the command ``omnic.pl -l -e -c -f FooBar.mdl'', five Java source and 2 byte code files are generated. Byte code is generated for the Foo and Bar base message classes, which are ready to use without modification by the developer. The other 5 files are skeletal source code which the developer must edit to perform useful and appropriate actions.

The first file is FooBase.java. The compiler generates import statements as follows. First it copies all global import statements (in this case there is a single global import, java.net.*), then all local import statmements are copied (in this case there is a single local import, java.util.BitSet), then the omnisockets.OmniMessage class is imported automatically (do not include a global or local import statement for omnisockets.OmniMessage). Once the import statements are written, omnic generates an abstract base class for message type Foo. Data members seq, bitmap and url are written to FooBase.java, public acccess methods are also written. Finally the toString() method is written.

/*
 * This file was generated by omnic version 1.0.beta.1
 * on Wed Dec  4 22:30:13 2002.
 * Do not modify.
 */

import java.net.*;
import java.util.BitSet;
import omnisockets.OmniMessage;

public abstract class FooBase extends OmniMessage {

    private int seq;
    public int getSeq() { return seq; }
    public void setSeq(int v) { seq = v; }

    private BitSet bitmap;
    public BitSet getBitmap() { return bitmap; }
    public void setBitmap(BitSet v) { bitmap = v; }

    private URL url;
    public URL getUrl() { return url; }
    public void setUrl(URL v) { url = v; }

    public String toString() {
        return "Foo[" + seq + ":" + bitmap + ":" + url + "]";
    }
}
    

Figure 3. FooBase.java

Then omnic generates a skeletal Foo class consisting of nothing but an empty run() method. You must modify the run() method of the Foo class to handle received messages of type Foo. Note that when the compiler is invoked, it checks for the existance of the Foo.java file, and if so won't overwrite it by default. Thus, if you change the mdl file and rerun omnic, it will not destroy any of your modified files. BTW, you can force the compiler to overwrite leaf classes with the -f option.

/*
 * This file was generated by omnic version 1.0.beta.1
 * on Wed Dec  4 22:30:14 2002.
 */

import omnisockets.*;

public class Foo extends FooBase {

    /**
     * The OmniSocket runs this message handler in a thread
     * when a message of type Foo is received.
     */
    public void run() {
    }
}
    

Figure 4. Foo.java

The same procedure is repeated for the Bar message block. First, file BarBase.java is created. Omnic copies the global import java.net.* and the default import omnisockets.OmniMessage (there are no local import statmements in the Bar block), then generates an abstract FooBase class with data members flag and inaddr, their public acccess methods, and a toString() method.

/*
 * This file was generated by omnic version 1.0.beta.1
 * on Wed Dec  4 23:22:53 2002.
 * Do not modify.
 */

import java.net.*;
import omnisockets.OmniMessage;

public abstract class BarBase extends OmniMessage {

    private boolean flag;
    public boolean getFlag() { return flag; }
    public void setFlag(boolean v) { flag = v; }

    private InetAddress inaddr;
    public InetAddress getInaddr() { return inaddr; }
    public void setInaddr(InetAddress v) { inaddr = v; }

    public String toString() {
        return "Bar[" + flag + ":" + inaddr + "]";
    }
}
    

Figure 5. BarBase.java

Then, omnic generates a skeletal Bar class, which like Foo.java above, contains nothing by an empty run() method.

/*
 * This file was generated by omnic version 1.0.beta.1
 * on Wed Dec  4 22:30:15 2002.
 */

import omnisockets.*;

public class Bar extends BarBase {

    /**
     * The OmniSocket runs this message handler in a thread
     * when a message of type Bar is received.
     */
    public void run() {
    }
}
    

Figure 6. Bar.java

Lastly, omnic generates a skeletal FooBar event handler class. Objects of this class are created and run by the OmniSockets framework whenever a socket event occurs. The generated event handler class consists of 7 methods. The run() method is first invoked when the event handler thread is started. The super.run() method implemented in OmniEventHandler simply switches on event code and calls one of four other methods. Connected(), disconnected(), message() or badMessage(), corresponding to generated events. Connected() and disconnected() methods are self explanatory. The message() method is called when any OmniMessage is received by an OmniSocket; you probably don't want to place type specific code here, rather any generic processing like message logging is appropriate for this method. The badMessage() method is called when an unrecognizable message is received on an OmniSocket. Your handler can obtain the source IP address and port, but unfortunately bad message data is not available for examination. The final two methods (the class constructor and the clone() method) must not by modified by the developer.

/*
 * This file was generated by omnic version 1.0.beta.1
 * on Wed Dec  4 23:22:54 2002.
 */

import omnisockets.*;

public class FooBarEventHandler extends OmniEventHandler {

    /**
     * The OmniSockets framework runs this event handler in
     * a thread when an OmniSockets framework event occurs.
     * You MAY modify or delete this method.
     */
    public void run() {
        super.run();
    }

    /**
     * Overrides the default CONNECT event handler.
     * You MAY modify or delete this method.
     */
    public void connected() {
        super.connected();
    }

    /**
     * Overrides the default DISCONNECT event handler.
     * You MAY modify or delete this method.
     */
    public void disconnected() {
        super.disconnected();
    }

    /**
     * Overrides the default MESSAGE event handler.
     * You MAY modify or delete this method.
     */
    public void message() {
        super.message();
    }

    /**
     * Overrides the default BAD_MESSAGE event handler.
     * You MAY modify or delete this method.
     */
    public void badMessage() {
        super.badMessage();
    }

    /**
     * Constructs a new event handler.
     * You MUST NOT modify this method.
     */
    public FooBarEventHandler(Object cookie) {
        super(cookie);
    }

    /**
     * Clones an event handler.
     * You MUST NOT modify this method.
     *
     * @return a duplicate FooBarEventHandler instance.
     */
    public Object clone() {
        return (Object) new FooBarEventHandler(getCookie());
    }
}
   

Figure 7. FooBarEventHandler.java

   

Copyright © 2002-2003, Richard Bockenek

SourceForge.net logo