Capturing native code output from within a Java process

In Java you can change the out and err output streams using System.setOut() and System.setErr() respectively. You could, for example, redirect that output to a log file. If you have native code wrapped in JNI, however, these calls do not affect the native standard output and standard error file descriptors. If your native code writes log lines to standard output there is no way to capture and redirect that output from within Java. First we’ll demonstrate the problem by creating a simple JNI method that writes output using the printf function.

package iocapture;

public class NativePrinter {
   /** Write message to native stdout file descriptor */
   public static native void print(String msg);
}

And the native code to implement our print method:

#include <stdio.h>
#include <jni.h>
#include "NativePrinter.h"

JNIEXPORT void JNICALL Java_iocapture_NativePrinter_print(JNIEnv *env, jclass class, jstring msg) {
    const char *nativeString = (*env)->GetStringUTFChars(env, msg, 0);
    printf("native[%s]\n", nativeString);
    fflush(stdout);
    (*env)->ReleaseStringUTFChars(env, msg, nativeString);
}

To demonstrate we can’t capture the native output, we’ll set System.out to a ByteArrayOutputStream and write to System.out and native standard output:

package iocapture;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

public class MainFail {
   static {
      System.load("/Users/tabbott/NativeIoCapture/dist/Debug/GNU-MacOSX/libNativeIoCapture.dylib");
   }

   public static final void main(String[] args) {
      ByteArrayOutputStream os = new ByteArrayOutputStream();
      System.setOut(new PrintStream(os));
      System.out.println("Hi from java code");
      System.err.println("Got \"" + new String(os.toByteArray()) + "\" from System.out");
      os.reset();
      NativePrinter.print("Hi from native code");
      System.err.println("Got \"" + new String(os.toByteArray()) + "\" from System.out");
   }
}

You can see that the output to System.out went to our ByteArrayOutputStream but the output from the native code (in black) was not affected.

In order to capture that output, we can extend InputStream and use the old trick of creating a pipe and using dup2 to duplicate the output descriptor over STDOUT. First the Java class:

package iocapture;

import java.io.IOException;
import java.io.InputStream;

public class NativeInputStream extends InputStream {
   public static final int STDOUT = 1;
   public static final int STDERR = 2;

   public NativeInputStream(int nativeFileNo) throws IOException   {
      init(nativeFileNo);
   }

   /** Create a pipe to capture output from native stdout file descriptor into this InputStream */
   private native void init(int nativeFileNo) throws IOException;

   @Override
   public native int read() throws IOException;

   @Override
   public native void close() throws IOException;
}

The native C code:

#include <stdio.h>
#include <unistd.h>
#include <jni.h>
#include "NativeInputStream.h"

int filedes[2];

JNIEXPORT void JNICALL Java_iocapture_NativeInputStream_init(JNIEnv *env, jobject obj, jint fileno) {
    if (-1 == pipe(filedes))
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "pipe failed");
    if (-1 == dup2(filedes[1], fileno)) {
        close(filedes[0]);
        close(filedes[1]);
        (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "dup2 failed");
    }
}

JNIEXPORT jint JNICALL Java_iocapture_NativeInputStream_read(JNIEnv *env, jobject obj) {
    char buf;
    ssize_t res = read(filedes[0], &buf, 1);
    switch (res) {
        case 0:
            return -1;
        case -1:
            (*env)->ThrowNew(env, (*env)->FindClass(env, "java/io/IOException"), "read failed");
            break;
        default:
            return buf;
    }
}

JNIEXPORT void JNICALL Java_iocapture_NativeInputStream_close(JNIEnv *env, jobject obj) {
    close(filedes[0]);
    close(filedes[1]);
}

And finally we can see it in action:

package iocapture;

import java.io.IOException;
import java.io.InputStream;

public class Main {
   static {
      System.load("/Users/tabbott/NativeIoCapture/dist/Debug/GNU-MacOSX/libNativeIoCapture.dylib");
   }

   public static void main(String[] args) throws IOException {
      // Capture output to native stdout file descriptor.
      // System.out is written to the native stdout file descriptor so all captured output
      // is written to System.err in order to avoid creating a loop!
      final InputStream is = new NativeInputStream(NativeInputStream.STDOUT);
      Thread t = new Thread() {
         @Override
         public void run() {
            // Tempting to wrap it in a BufferedReader and InputStreamReader and just use readLine()
            // but InputStreamReader seems to have a large internal buffer which it must fill before
            // you get anything out of it.
            StringBuilder s = new StringBuilder();
            while (true) {
               try {
                  int ch = is.read();
                  if (-1 == ch)
                     break;
                  if ('\n' == ch) {
                     System.err.println("Read from native handle: \"" + s + "\"");
                     s.setLength(0);
                  } else {
                     s.append((char)ch);
                  }
               }
               catch (IOException ex) {
                  ex.printStackTrace();
               }
            }
            System.err.println("EOF");
         }
      };
      t.setDaemon(true);
      t.start();

      // and finally some output to capture
      NativePrinter.print("Hello, world!");
   }
}

This example was written and tested using Java 1.6 on MacOS X and should work on any UN*X platform. I’m not familiar with the Windows APIs but I’m sure something similar is possible there.

A simple SVG Widget for the NetBeans Platform Visual Library

The Visual Library API provides a great tool for visualising data. You can render images with ImageWidget but, unsurprisingly, they don’t look very good when zooming into the scene. Visual Library doesn’t support vector images such as SVG out of the box, but fortunately rolling your own SVG Widget is fairly straight forward.

To render the SVG images I’ve used the Apache Batik library. Since my application is a NetBeans Platform application I’ve wrapped the library in a Module. Not being too familiar with Batik I just went with the brute force approach of wrapping all the jars supplied in the lib directory of the binary distribution – overkill, I’m sure. Some of these jars contain classes that conflict with others in the NetBeans Platform, so after a bit of trial-and-error I settled on publishing (via API Versioning) the packages org.w3c.dom.svg and everything under org.apache.batik.

How to render a SVG file to a Graphics2D context seems to be undocumented but fortunately I found this forum post which provided me with the correct incantation for the job. The resultant SvgWidget class looks like this:

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.io.IOException;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.dom.svg.SAXSVGDocumentFactory;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.util.XMLResourceDescriptor;
import org.netbeans.api.visual.widget.Scene;
import org.netbeans.api.visual.widget.Widget;
import org.w3c.dom.svg.SVGDocument;

public class SvgWidget extends Widget {
    private GraphicsNode rootGN;

    public SvgWidget(Scene scene, String uri) throws IOException {
        super(scene);
        setSvgUri(uri);
    }

    public SvgWidget(Scene scene) {
        super(scene);
    }

    public void setSvgUri(String uri) throws IOException {
        SAXSVGDocumentFactory df = new SAXSVGDocumentFactory(
                 XMLResourceDescriptor.getXMLParserClassName());
        SVGDocument doc = df.createSVGDocument(uri);
        UserAgent userAgent = new UserAgentAdapter();
        DocumentLoader loader = new DocumentLoader(userAgent);
        BridgeContext ctx = new BridgeContext(userAgent, loader);
        ctx.setDynamicState(BridgeContext.DYNAMIC);
        GVTBuilder builder = new GVTBuilder();
        rootGN = builder.build(ctx, doc);
        revalidate();
    }

    @Override
    protected Rectangle calculateClientArea() {
        return rootGN.getBounds().getBounds();
    }

    @Override
    protected void paintWidget() {
        if (null != rootGN) {
            Graphics2D g = getGraphics();
            rootGN.paint(g);
        }
    }
}

This is, of course, a very simple implementation. You’re likely to have multiple instances loading the same SVG file which would result in a lot of unnecessary XML parsing and data duplication. Adding a shared cache of GraphicsNodes would be a useful enhancement to start with.

Tethering the iPhone on MTN

Feeling brave, I upgraded my iPhone 3GS to iOS4 last night. So far I’m liking it. Everyone’s been talking about all the cool new features but nobody seems to have picked up on the fact that tethering is no longer the exclusive preserve of Vodacom subscribers and jailbreakers. Something that MTN subscribers have been longing for is now configurable directly in the phone settings, no need for special carrier settings files or other magical incantations.

To enable tethering, go to Settings > General > Network > Cellular Data Network and enter an APN name under Internet Tethering. Leave the username and password blank, an APN name is all that’s required to enable the tethering option.

When you return to the Network options the Internet Tethering option should appear. (You might need to go back to General and back into Network before you see it.) Turn it on, select USB or Bluetooth tethering and follow the on-screen instructions.