You are viewing outdated content for BUG. If you have a BUG Y.T. edition or 2.0 series device, please visit our updated wiki: http://wiki.buglabs.net



JNotify

From BUG Wiki

Jump to: navigation, search

Contents

Overview

This page describes how to access the JNotify API from a BUGapp. The JNotify API was added in BUG Release 1.4.3. JNotify was written by Omry Yadan. See the JNotify project page for more details.

inotify Overview

inotify is a Linux Kernel mechamism for file system change notification in userspace applications. A simple use case is that an application wishes to perform some action based on the contents of a file. Some other process may modify the file. The application could poll, read the file and check to see if there are changes on some periodic interval. Or the application could register its interest with the kernel. If any changes occur to the file, the application is notified. This is a better approach because the application can safely be inactive until a notification occurs.

JNotify Overview

JNotify is a JNI wrapper to the inotify subsystem. It allows BUGapp (OSGi bundles) to register for notification of file changes. The JNotify API is very simple.

JNofity API

A client application has essentially to points of contact with the JNotify API. Event registration and notification.

Register for Notification

From javadoc:

 int addWatch(java.lang.String path, int mask, boolean watchSubtree, JNotifyListener listener)         
 boolean removeWatch(int wd)

Handle Notification

From javadoc:

 void 	fileCreated(int wd, java.lang.String rootPath, java.lang.String name)
 void 	fileDeleted(int wd, java.lang.String rootPath, java.lang.String name)        
 void 	fileModified(int wd, java.lang.String rootPath, java.lang.String name)         
 void 	fileRenamed(int wd, java.lang.String rootPath, java.lang.String oldName, java.lang.String newName)

Examples

Changes to Home Directory

Here is a Bundle Activator that registers an inner class as a listener to the root user's home directory, and simply prints a message to system out when a file system notification occurs:

 public class Activator implements BundleActivator {
 	int handle = -1;
 
 	public void start(BundleContext context) throws Exception {
 		handle = JNotify.addWatch("/home/root", JNotify.FILE_ANY, false, new JNotifyListener() {
 
 			public void fileCreated(int wd, String rootPath, String name) {
 				System.out.println("Created " + name);
 			}
 
 			public void fileDeleted(int wd, String rootPath, String name) {
 				System.out.println("Deleted " + name);
 			}
 
 			public void fileModified(int wd, String rootPath, String name) {
 				System.out.println("Modified " + name);
 			}
 
 			public void fileRenamed(int wd, String rootPath, String oldName,
 					String newName) {
 				System.out.println("Renamed " + oldName);
 			}
 
 		});
 	}
 
 	public void stop(BundleContext context) throws Exception {
 		if (handle > -1) {
 			JNotify.removeWatch(handle);
 		}
 	}
 }

This application is available on BUGnet.

Kernel Log Event Notifier

Let's create a bundle that offers an OSGi service for kernel log events. Client bundles may be interested in things like USB device attachment notices, networking events, or other system events. One could implement this functionality with polling; simply check the contents of /var/log/messages every few minutes and if there is some new data pass it to clients. A more efficient approach is to use the inotify API. First we'll define our OSGi service:

 public interface IKernelLogService {
 
 	public void register(IKernelLogListener listener, String filter);
 
 	public void unregister(IKernelLogListener listener);
 }

Ah, it's just a basic listener interface. We pass in a filter so that clients can filter out unnecessary input. In our example implementation we'll just check for the occurrence of the contents of filter in the log output. If there is an occurrence it will be a match and passed to the client. A more useful implementation might use regex or at least wildcards.

And the actual listener is pretty basic as well:

public interface IKernelLogListener {
	public void notify(String message);
}

These two interfaces will go into a public package that we will export via the OSGi manifest file. Now in our bundle activator we create an instance of IKernelLogService and register it with the OSGi service registry:

 public class Activator implements BundleActivator {
 	private ServiceRegistration sr = null;
 
 	public void start(BundleContext context) throws Exception {
 		sr = context.registerService(IKernelLogService.class.getName(), new KernelLogService(), null);
 	}
 
 	public void stop(BundleContext context) throws Exception {
 		if (sr != null) {
 			sr.unregister();
 		}
 	}
 }

And finally we implement the service. First we register ourselves with JNotify so that we can tell when new kernel log events have occurred. Next we load the log file, and scan to the end of it. Then we just sit and wait until inotify tells us the file has changed. We load the changes, match against the listener filters, and notify the relevant clients.

 public class KernelLogService implements IKernelLogService, JNotifyListener {
 
 	private Map listeners;
 	private BufferedReader br;
 
 	public KernelLogService() throws IOException {
 		JNotify.addWatch("/var/log/messages", JNotify.FILE_MODIFIED, false,
 				this);
 		listeners = new Hashtable();
 		br = new BufferedReader(new FileReader(new File("/var/log/messages")));
 		scanToEnd(br);
 	}
 
 	private void scanToEnd(BufferedReader br2) throws IOException {
 		while (br2.readLine() != null);
 	}
 
 	public void register(IKernelLogListener listener, String filter) {
 		listeners.put(listener, filter);
 	}
 
 	public void unregister(IKernelLogListener listener) {
 		listeners.remove(listener);
 	}
 
 	public void fileCreated(int wd, String rootPath, String name) {
 		//Ignore
 	}
 
 	public void fileDeleted(int wd, String rootPath, String name) {
 		//Ignore
 	}
 
 	public void fileModified(int wd, String rootPath, String name) {
 		try {
 			String line = br.readLine();
 
 			for (Iterator i = listeners.entrySet().iterator(); i.hasNext();) {
 				Map.Entry entry = (Map.Entry) i.next();
 				IKernelLogListener l = (IKernelLogListener) entry.getKey();
 				String filter = (String) entry.getValue();
 
 				if (line.indexOf(filter) > -1) {
 					l.notify(line);
 				}
 			}
 		} catch (IOException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}
 	}
 
 	public void fileRenamed(int wd, String rootPath, String oldName,
 			String newName) {
 		//Ignore
 	}
 }

This application is available on BUGnet.

Caveats

  • inotify only will generate notifications on 'real' files. This excludes the /sys filesystem.