This Community site is new! Please help us build a community around the JNIOR.
Sign up, and help share your knowledge. Please sign-up even if you do not plan to post as a sign of support.
If there is evidence of a demand we will continue to develop the content here.

Message Pump Sample

You got ideas? Let's hear 'em. Here we can talk about your experiences programming on the JNIOR or items that you may wish to have INTEG assist you with.
Post Reply
kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Message Pump Sample

Post by kmcloutier » Thu Oct 12, 2017 2:14 pm

There is a message pump on the JNIOR that enabled communication between the Operating System and JAVA applications. There are many messages that are sent by the Operating System.

System_and_Process_Messaging.pdf
(882.75 KiB) Downloaded 32 times

They are very helpful when it comes to certain events. For example, a registry key is updated. Instead of reading a registry key over and over waiting for a change we can watch the message pump. When we see a registry update message we can check to see if the registry key that is provided as the message is one that we care about.

Here is a sample application that shows how to read the message pump and display some information about the message that was transmitted.

package com.integpg.messagepumpsample;

/**
 * HexUtils will give us access to the hex dump so that we can show a meaningful representation of the data in each
 * message
 */
import com.integpg.janoslib.utils.HexUtils;
/**
 * System classes pertaining to the message pump itself and the class that will be returned from the getMessage call
 */
import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
/**
 * A class that will format a date string quickly
 */
import java.text.QuickDateFormat;

/**
 * A class to demonstrate the Message Pump feature in the JNIOR.
 */
public class MessagePumpSample {

    /**
     * the QuickDateFormat() class is part of a library of classes and are beyond the scope of this example.
     */
    private final static QuickDateFormat QUICK_DATE_FORMAT = new QuickDateFormat("MM-dd-yy HH:mm:ss.fff");

    /**
     * a static list of all of the known messages
     */
    public static final int SM_SHUTDOWN = 0x01;
    public static final int SM_PROBE = 0x02;
    public static final int SM_GCRUN = 0x10;
    public static final int SM_WATCHDOG = 0x11;
    public static final int SM_SYSLOG = 0x12;
    public static final int SM_REGUPDATE = 0x40;
    public static final int SM_WEBSTARTUP = 0x60;
    public static final int SM_WEBSHUTDOWN = 0x61;
    public static final int SM_PROTCMDMSG = 0x70;
    public static final int SM_PROTCMDRESP = 0x71;
    public static final int SM_PIPEOPEN = 0x80;
    public static final int SM_PIPECLOSE = 0x81;
    public static final int SM_USER = 0x400;

    private final MessagePump _messagePump = new MessagePump();



    public static void main(String[] args) {
        MessagePumpSample msgPumpSample = new MessagePumpSample();
        msgPumpSample.init();
    }



    public void init() {
        System.out.println("open the message pump and start monitoring.\r\n");
        _messagePump.open();

        for (;;) {
            // read all messages from the message pump
            SystemMsg systemMsg = _messagePump.getMessage();

            // we must repost as fast as we can
            _messagePump.postMessage(systemMsg);

            // the QUICK_DATE_FORMAT.format() call is part of a library of classes and are beyond the scope of this 
            // example.
            String dateString = QUICK_DATE_FORMAT.format(System.currentTimeMillis());
            // so we have a message.  what kind is it?
            String messageName = getMessageNameByType(systemMsg.type);
            // print out what we know about this message
            System.out.println(dateString + " - Process Msg: " + messageName);

            // if there is data in the message then we should dump it so that we can see what it is.
            if (0 < systemMsg.msg.length) {
                // the HexUtils.hexDump() call is part of a library of classes and are beyond the scope of this 
                // example.
                String hexDumpString = HexUtils.hexDump(systemMsg.msg);
                System.out.println(hexDumpString);
            }
        }

    }



    /**
     * @return a String definition based on the type parameter
     */
    public static String getMessageNameByType(int type) {
        switch (type) {
            case SM_SHUTDOWN:
                return "Shutdown";
            case SM_PROBE:
                return "Probe";
            case SM_GCRUN:
                return "GC Run";
            case SM_WATCHDOG:
                return "Watchdog";
            case SM_SYSLOG:
                return "Syslog";
            case SM_REGUPDATE:
                return "Registry Update";
            case SM_WEBSTARTUP:
                return "Web Server Startup";
            case SM_WEBSHUTDOWN:
                return "Web Server Shutdown";
            case SM_PROTCMDMSG:
                return "Protocol Command Message";
            case SM_PROTCMDRESP:
                return "Protocol Command Reesponse";
            case SM_PIPEOPEN:
                return "Pipe Opened";
            case SM_PIPECLOSE:
                return "Pipe Closed";
            default:
                if (type >= SM_USER) return "User Defined Type " + String.valueOf(type);
                else return "Unknown Type " + String.valueOf(type);
        }
    }

}

Here is some output that we get from executing the sample.
message-pump-output.png
message-pump-output.png (31.96 KiB) Viewed 821 times
I am a Senior Software Programmer at INTEG. You have questions and I have answers.

bscloutier
Posts: 401
Joined: Thu Sep 14, 2017 12:55 pm

Re: Message Pump Sample

Post by bscloutier » Mon Oct 16, 2017 5:47 pm

Kev,

I like the new code display. Maybe it can use the vertical scroll bar though.

The message pump supports inter-process communications which can be useful in the multi-tasking JANOS environment. Maybe you can show us a simple example of how to use the messaging to communicate with a dynamic webpage? That can be key in creating GUI and HMI interfaces.

kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Re: Message Pump Sample

Post by kmcloutier » Tue Oct 17, 2017 10:55 am

Scroll bar implemented.

I was going to update the sample to "classify" this message pump sample. An update to this example would allow for multiple listeners to get alerted of received messages. Each listener would then decide if it cares about the message type and ultimately the message data. This could then be used in the web HMI example to you talk about. But I feel that examples like this kind of feel lost inside the forums.
I am a Senior Software Programmer at INTEG. You have questions and I have answers.

kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Re: Message Pump Sample

Post by kmcloutier » Tue Oct 17, 2017 1:35 pm

The current sample above uses the MessagePumpSample class to contain all of the code. I want to utilize classes to make this code more useful to other projects. We will be extracting code from our current main class and leaving the new MessagePumpSampleMain class for setting up our other classes.

Here is a quick list of the classes that we will implement. The source will be listed below.

I will start by creating a MessagePumpEngine class. This class will be responsible for watching the message pump and alerting all listeners when a message is received.

Since we will have listeners we will need a listener interface. Let's create a MessagePumpListener interface.

A message type class will be implemented that will contain an enumeration of values for each message type. This class should also provide a nice method for returning a description for each message type. We will call this class SystemMessageTypes.

To show how we can create a special class that cares about a certain message type we will make a RegistryMessageHandler class. As the name suggests, it will care about registry update messages.

A quick overview of the classes that will be listed below:
  • MessagePumpSampleMain
  • MessagePumpEngine
  • MessagePumpListener
  • SystemMessageTypes
  • RegistryMessageHandler

MessagePumpSampleMain
package com.integpg.messagepumpsample;

/**
 * HexUtils will give us access to the hex dump so that we can show a meaningful representation of the data in each
 * message
 */
import com.integpg.janoslib.utils.HexUtils;
/**
 * System classes pertaining to the message pump itself and the class that will be returned from the getMessage call
 */
import com.integpg.system.SystemMsg;
/**
 * A class that will format a date string quickly
 */
import java.text.QuickDateFormat;

/**
 * A class to demonstrate the Message Pump feature in the JNIOR.
 */
public class MessagePumpSampleMain implements MessagePumpListener {

    /**
     * the QuickDateFormat() class is part of a library of classes and are beyond the scope of this example.
     */
    private final static QuickDateFormat QUICK_DATE_FORMAT = new QuickDateFormat("MM-dd-yy HH:mm:ss.fff");



    public static void main(String[] args) throws InterruptedException {
        // instantiate our main class
        MessagePumpSampleMain msgPumpSample = new MessagePumpSampleMain();
        msgPumpSample.init();

        // the main method has nothing left to do.  let it sleep forever.  Most if not all of our other threads 
        // are daemon threads that would not keep the process from sxiting if this main thread completes.  
        // Sometimes we might do somehting in this thread but for the sake of this example we have nothing to do.
        Thread.sleep(Integer.MAX_VALUE);
    }



    public void init() {
        // the message pump engine is a singleton.  We do not have to instatiate another instance.  We can just 
        // use static methods to interface with it.  
        //
        // first we need to add in any listeners that should be alerted when a new message is received.  It 
        // will be up to those llisteners to determine if it cares about the message.
        MessagePumpEngine.addListener(this);
        MessagePumpEngine.addListener(new RegistryMessageHandler());

        // we are ready.  start listening
        MessagePumpEngine.start();
    }



    @Override
    public void messageReceived(SystemMsg systemMsg) {
        // the QUICK_DATE_FORMAT.format() call is part of a library of classes and are beyond the scope of this 
        // example.
        String dateString = QUICK_DATE_FORMAT.format(System.currentTimeMillis());
        // so we have a message.  what kind is it?
        String messageName = SystemMessageTypes.getMessageNameByType(systemMsg.type);
        // print out what we know about this message
        System.out.println(dateString + " - Process Msg: " + messageName);

        // if there is data in the message then we should dump it so that we can see what it is.
        if (0 < systemMsg.msg.length) {
            // the HexUtils.hexDump() call is part of a library of classes and are beyond the scope of this 
            // example.
            String hexDumpString = HexUtils.hexDump(systemMsg.msg);
            System.out.println(hexDumpString);
        }
    }

}


MessagePumpEngine
package com.integpg.messagepumpsample;

import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
import java.util.Vector;

public class MessagePumpEngine implements Runnable {

    private static final MessagePumpEngine THIS = new MessagePumpEngine();

    private final MessagePump _messagePump = new MessagePump();

    private static Thread _thread;
    private static final Vector<MessagePumpListener> _listeners = new Vector<>();



    /**
     * Singleton constructor
     */
    private MessagePumpEngine() {
    }



    /**
     * adds a listener that will be alerted of all received messages. The listener will be responsible for determining
     * if the message has meaning to them
     */
    public static void addListener(MessagePumpListener listener) {
        synchronized (_listeners) {
            _listeners.addElement(listener);
        }
    }



    /**
     * starts our message pump engine.
     */
    static void start() {
        if (null == _thread) {
            _thread = new Thread(THIS);
            _thread.setName("message-pump-engine");
            _thread.setDaemon(true);
            _thread.start();
        }
    }



    @Override
    public void run() {
        System.out.println("open the message pump and start monitoring.\r\n");
        _messagePump.open();

        for (;;) {
            // read all messages from the message pump
            SystemMsg systemMsg = _messagePump.getMessage();

            // we must repost as fast as we can
            _messagePump.postMessage(systemMsg);

            // notify all of our listeners
            synchronized (_listeners) {
                for (int i = 0; i < _listeners.size(); i++) {
                    MessagePumpListener listener = _listeners.elementAt(i);
                    listener.messageReceived(systemMsg);
                }
            }

        }
    }

}



MessagePumpListener
package com.integpg.messagepumpsample;

import com.integpg.system.SystemMsg;

/**
 * An interface that outlines the method or methods that all classes that implement this interface must define.
 */
public interface MessagePumpListener {

    public void messageReceived(SystemMsg systemMsg);
}



SystemMessageTypes
package com.integpg.messagepumpsample;

/**
 * A class that contains an enumeration of values for each message type.
 */
public class SystemMessageTypes {

    public static final int SM_SHUTDOWN = 0x01;
    public static final int SM_PROBE = 0x02;
    public static final int SM_GCRUN = 0x10;
    public static final int SM_WATCHDOG = 0x11;
    public static final int SM_SYSLOG = 0x12;
    public static final int SM_REGUPDATE = 0x40;
    public static final int SM_WEBSTARTUP = 0x60;
    public static final int SM_WEBSHUTDOWN = 0x61;
    public static final int SM_PROTCMDMSG = 0x70;
    public static final int SM_PROTCMDRESP = 0x71;
    public static final int SM_PIPEOPEN = 0x80;
    public static final int SM_PIPECLOSE = 0x81;
    public static final int SM_USER = 0x400;



    /**
     * @return a String definition based on the type parameter
     */
    public static String getMessageNameByType(int type) {
        switch (type) {
            case SM_SHUTDOWN:
                return "Shutdown";
            case SM_PROBE:
                return "Probe";
            case SM_GCRUN:
                return "GC Run";
            case SM_WATCHDOG:
                return "Watchdog";
            case SM_SYSLOG:
                return "Syslog";
            case SM_REGUPDATE:
                return "Registry Update";
            case SM_WEBSTARTUP:
                return "Web Server Startup";
            case SM_WEBSHUTDOWN:
                return "Web Server Shutdown";
            case SM_PROTCMDMSG:
                return "Protocol Command Message";
            case SM_PROTCMDRESP:
                return "Protocol Command Reesponse";
            case SM_PIPEOPEN:
                return "Pipe Opened";
            case SM_PIPECLOSE:
                return "Pipe Closed";
            default:
                if (type >= SM_USER) return "User Defined Type " + String.valueOf(type);
                else return "Unknown Type " + String.valueOf(type);
        }
    }
}



RegistryMessageHandler
package com.integpg.messagepumpsample;

import com.integpg.system.JANOS;
import com.integpg.system.SystemMsg;

public class RegistryMessageHandler implements MessagePumpListener {

    @Override
    public void messageReceived(SystemMsg systemMsg) {
        // we only care about registry key updates
        if (SystemMessageTypes.SM_REGUPDATE == systemMsg.type) {
            // get the key name from the message data
            String keyName = new String(systemMsg.msg);
            // now get teh new value
            String keyValue = JANOS.getRegistryString(keyName, "");
            System.out.println("Registry Update: " + keyName + " = " + keyValue);
        }
    }

}

I am a Senior Software Programmer at INTEG. You have questions and I have answers.

kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Re: Message Pump Sample

Post by kmcloutier » Tue Oct 17, 2017 3:18 pm

Now let's demonstrate how to create a simple Web HMI. By using the message pump we can send messages from a web interface to our java application.
web hmi sample.png
web hmi sample.png (35.19 KiB) Viewed 809 times
In the above sample the user will press the button to generate a random number. The JAVA application will receive the request, generate the number and return it to the web application!

Here is what the application output looks like for this transaction.
web hmi sample application output.png
web hmi sample application output.png (6.73 KiB) Viewed 808 times

We only have to add once class and one method to the above sample. We need a class that will process the incoming message. Once the message is processed and a response is generated we will need a new method in our MessagePumpEngine that will allow use to send back to the message Pump.

Here is the listing for the updated MessagePumpEngine. At the bottom is our added method for exposing the postMessage() method.
package com.integpg.messagepumpsample;

import com.integpg.system.MessagePump;
import com.integpg.system.SystemMsg;
import java.util.Vector;

public class MessagePumpEngine implements Runnable {

    private static final MessagePumpEngine THIS = new MessagePumpEngine();

    private static final MessagePump _messagePump = new MessagePump();

    private static Thread _thread;
    private static final Vector<MessagePumpListener> _listeners = new Vector<>();



    /**
     * Singleton constructor
     */
    private MessagePumpEngine() {
    }



    /**
     * adds a listener that will be alerted of all received messages. The listener will be responsible for determining
     * if the message has meaning to them
     */
    public static void addListener(MessagePumpListener listener) {
        synchronized (_listeners) {
            _listeners.addElement(listener);
        }
    }



    /**
     * starts our message pump engine.
     */
    static void start() {
        if (null == _thread) {
            _thread = new Thread(THIS);
            _thread.setName("message-pump-engine");
            _thread.setDaemon(true);
            _thread.start();
        }
    }



    @Override
    public void run() {
        System.out.println("open the message pump and start monitoring.\r\n");
        _messagePump.open();

        for (;;) {
            // read all messages from the message pump
            SystemMsg systemMsg = _messagePump.getMessage();

            // we must repost as fast as we can
            _messagePump.postMessage(systemMsg);

            // notify all of our listeners
            synchronized (_listeners) {
                for (int i = 0; i < _listeners.size(); i++) {
                    MessagePumpListener listener = _listeners.elementAt(i);
                    listener.messageReceived(systemMsg);
                }
            }

        }
    }



    /**
     * Exposes the postMesssage method of the System MessagePump
     * 
     * @param msg the message the will be sent to the system message pump
     */
    public static void postMessage(SystemMsg msg) {
        _messagePump.postMessage(msg);
    }
}



RandomNumberMessageHandler
package com.integpg.messagepumpsample;

import com.integpg.system.SystemMsg;

public class RandomNumberMessageHandler implements MessagePumpListener {

    @Override
    public void messageReceived(SystemMsg systemMsg) {
        // we only care about type 2000.  We know that on this jnior that only this application is going to 
        // use this message type.  This would present an issue if multiple applications tried to use the same 
        // messsage type.
        if (2000 == systemMsg.type) {
            // get the request string from the message
            String requestString = new String(systemMsg.msg);
            if ("get-random-number".equalsIgnoreCase(requestString)) {
                System.out.println("We received a request for a random number");

                // get a random number
                int randomNumber = (int) (Math.random() * 100);
                System.out.println("Return: " + randomNumber + "\r\n");

                // generate a response and post it to the message pump
                SystemMsg responseMsg = new SystemMsg();
                responseMsg.type = 2000;
                responseMsg.msg = String.valueOf(randomNumber).getBytes();
                MessagePumpEngine.postMessage(responseMsg);
            }
        }
    }

}

web hmi sample application output.png
web hmi sample application output.png (6.73 KiB) Viewed 808 times
I am a Senior Software Programmer at INTEG. You have questions and I have answers.

kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Re: Message Pump Sample

Post by kmcloutier » Wed Oct 18, 2017 10:17 am

Here is the web side for this application. It requires the comm.js and md5.js files from the DCP. You can extract those from the www.zip file that is stored on the JNIOR in the flash directory.
<!DOCTYPE html>
<html lang="en">

    <head>
        <base href="/messagepumpsample/">

        <title>Message Pump Web Interface</title>

        <script src="comm.js" type="text/javascript"></script>
        <script src="md5.js" type="text/javascript"></script>

        <style>
            body { padding: 5px; }
        </style>

    </head>


    <body>

        <div>

            <h1>Message Pump Web Interface</h1>

            <button id="random-number-button">Get Random Number</button>
            <p style="width: 600px;">
                Clicking the button above will cause a message to get sent to the Web Server.  The Web Server 
                then places it in the System Message Pump.  Our sample Java application will then receive it and 
                for the sake of the sample will respond with a random number.  By placing the random number in a 
                message and back on to the System Message Pump with the same message type that it received, the 
                Operating System knows to send it back to the Web Server and in turn back to the sending Web 
                Socket connection.
            </p>

        </div>


        <script type="text/javascript">
            // our websocket communication object
            var _comm;
            var _loggedIn = false;

            // method called when the connection is successfully opened
            var onopen = function () {
                console.log("comm opened");
            };
            
            // method called when our websocket object receives a message from the web server
            var onmessage = function (evt) {
                var json = JSON.parse(evt.data);
                console.log(json);

                if (json.Message === "Monitor") {
                    if (!_loggedIn) {
                    }
                    _loggedIn = true;
                }

                //
                else if (json.Message === "Reply Message") {
                    if (2000 === json.Number) {
                        alert("Your Random Number is " + json.Content);
                    }
                }
            };
            
            // instantiate our websocket communications object and wire up some methods.  lastly call connect!
            _comm = new Comm();
            _comm.onopen = onopen;
            _comm.onmessage = onmessage;
            _comm.connect();


            
            // register an onclick method so that we can post a message to get a random number from our 
            // sample java application.
            document.getElementById('random-number-button').onclick = function (e) {
                var getUpdates = {Message: "Post Message", Number: 2000, Content: "get-random-number"};
                _comm.sendJson(getUpdates);
            };
        </script>
    </body>

</html>
I am a Senior Software Programmer at INTEG. You have questions and I have answers.

kmcloutier
Posts: 31
Joined: Tue Sep 12, 2017 7:26 am

Re: Message Pump Sample

Post by kmcloutier » Wed Oct 18, 2017 12:22 pm

2017-10-18_10-06-23.mp4
Video of the sample in action
(199.3 KiB) Downloaded 32 times
I am a Senior Software Programmer at INTEG. You have questions and I have answers.

Post Reply