Events and Event Processing

NeuroKernel OS is an event based system. Everything is handled via event flows between modules. Because of this, NeuroKernel has a very specialized event mechanism. Events are handled via special purpose event listeners. IEventListener and IDataListener are the two widely used general purpose event handler interfaces used by the system. There are also other listeners for the specific purposes such as IServiceListener and IPortListener. It is important to understand this event system before writing any NeuroKernel applications. This event model used is very well known by many Java and JavaScript developers.

1. Event Pipeline

When an event sent to an event dispatcher, it processes the event and calls a event multicaster to announce the event to the listeners. Each task has an event pipeline that is controlled by the task container. Events are served to their destinations by their type and event properties if any special treatment needed. Task container will dispose an event if the destination of the event object is not know or unavailable. In the below example, slider value is read by an action listener of a button. When this button is pushed and released, an action event is created and delivered to the application that hosts this button. Task container looks for the button and dispatches the event to the button component. In the last step, button component receives the event and after processing it calls the registered event listeners.

import com.neurokernel.client.*;
import com.neurokernel.adapter.NActionListener;
 
public class EventListenerExample extends NApplication {
      private NFrame mainFrame;
      private NSlider slider;
 
      @Override
      public void main(int argc, String[] argv) {
          mainFrame = getMainFrame();
          NGridLayout gridLayout=mainFrame.getLayout();
          gridLayout.setSize(2,1);
 
          slider = new NSlider(mainFrame, Orientation.HORIZONTAL, 0, 255, 0);
 
          new NButton(mainFrame,"Get Value").addListener(new IEventListener() {
              @Override
              public void onEvent(NEvent event) {
                  if (event.getEventType() == NEventTypes.ACTION) {
                        NMessageDialog.showInfo(mainFrame, slider.getValue());
                  }
              }
          });
 
          mainFrame.setTitle("Listener Example");
          mainFrame.show(20,20);
      }
}

As you see, and general purpose event listener is used for this example. Event pipeline does not care about the type of the event, it is sent to the destination in raw form packaged in a NEvent object.

2. Observers

UI controls also offer another listener type called observer. Observer's only purpose is to get the current value of the UI control. It is possible to write bean frameworks based on the observer features of the UI controls. All UI controls in NeuroKernel implement IObserver interface by default.

import com.neurokernel.client.*;
import com.neurokernel.adapter.NActionListener;
 
public class ObserverListenerExample extends NApplication {
      private NFrame mainFrame;
      private NSlider slider;
 
      @Override
      public void main(int argc, String[] argv) {
          mainFrame = getMainFrame();
          NGridLayout gridLayout=mainFrame.getLayout();
          gridLayout.setSize(3,1);
 
          NTextField textfield = new NTextField(mainFrame);
          slider = new NSlider(mainFrame, Orientation.HORIZONTAL, 0, 255, 0);
          slider.addObserver(textfield);
 
          new NButton(mainFrame,"Exists?").addListener(new IEventListener() {
              @Override
              public void onEvent(NEvent event) {
                  if (event.getEventType() == NEventTypes.ACTION) {
                      NMessageDialog.showInfo(mainFrame, slider.getValue());
                  }
              }
          });
 
          mainFrame.setTitle("Listener/Observer Example");
          mainFrame.show(20,20);
      }
}

3. Data listeners

Data listeners are used widely along with event listeners to fetch the data resulting from a system call. They are used mostly by calls to the kernel modules. The response object includes all the information about the incoming data package. The API reference manual gives details about the data to be read for each data listener passed to a method that calls a system interface.

import com.neurokernel.client.*;
import com.neurokernel.client.io.*;
import com.neurokernel.adapter.NActionListener;
 
public class DataListenerExample extends NApplication {
      @Override
      public void main(int argc, String[] argv) {
          final NFrame mainFrame = getMainFrame();
 
          new NButton(mainFrame,"Get Date").addListener(new NActionListener() {
              @Override
              public void onAction(NEvent e) {
                  NFile myFile = new NFile("/myfile.txt");
                  IFileSystem fileSystem=myFile.getFileSystem(getSystem());
                  fileSystem.exists(myFile, new IDataListener() {
                      @Override
                      public void onSuccess(IResponse connection) {
                          NMessageDialog.showInfo(mainFrame,"File exists");
                      }
 
                      @Override
                      public void onFailure( IResponse connection) {
                          NMessageDialog.showError(mainFrame,"File does not exists");
                      }
                  });
              }
          });
 
          mainFrame.setTitle("IDataListener Interface");
          mainFrame.setBounds(20,20,200,200);
          mainFrame.setVisible(true);
      }
}