header

Torsten Curdt’s weblog

From push to pull with javaflow

Just recently Marcus blogged about how Sam turned expat into a pull parser via ruby continuations. I found that pretty interesting and was wondering if you could do the same with javaflow. So let’s run through this little program sketch…

A push parser basically is just (more or less) a long running process. It becomes the runnable that we pass into the Continuation class.


public class Parsing extends Runnable {
  public void run() {
    ...
    parser.setContentHandler(new ContinuationSaxHandler());
    parser.parse(inputSource);
  }
}

In order to turn this into a pull scenario we have another place of action where we explicitly ask for every event. That's the actuall pull loop.


while(true) {
  final Event e = c.pull();
  if (e != null) break;
    ...
}

Now how can we connect these two processes? As you can see the parser already uses a special ContinuationSaxHandler.


public class ContinuationSaxHandler extends DefaultHandler {

  public interface Event {
  }

  public class StartElementEvent implements Event {
    String qName;
  }

  public void startElement( String nsURI, String localName, String qName, Attr...
    ContinuationWrapper w = (ContinuationWrapper) Contiuation.getContext();
    w.result = new StartElementEvent(qName);
    Continuation.suspend();
  }
}

This special ContentHandler receives the events from the SAX parser and transforms them into Events objects. These objects need to get passed along when the execution returns at the suspend to the pull loop. In order to pass on the object, the Continuation needs to be wrapped in a ContinuatonWrapper that also provides the actual "pull" method and provides access to the Event object.


public class ContinuationWrapper {

  Continuation c;
  Event result;

  public ContinuationWrapper( Continuation c ) {
    this.c = c;
  }

  public Event pull() {
    result = null;
    c = Continuation.continueWith(c, this);
    return result;
  }
}

So in order to start the pull parsing we only need to create a ContinuationWrapper around the first continuation.


c = new ContinuationWrapper(Continuation.startWith(new Parsing(inputSource)));

while(true) {
  final Event e = c.pull();
  if (e != null) break;
    ...
}

So in theory this should work just fine. I am a bit concerned about the overhead though. The complete parser would need to be instrumented. Plus we have an external resource that is not that easy to "freeze" - the file descriptor. It's an external resource an therefor should not be part of the continuation. All such external resources should usually be passed in through the context object that can be provided on the startWith/continueWith calls.

Now what really blows me away is the fact that in ruby this obviously seems to work also for native code. (expat is native) So I would be really interested why/how this actually works. What happens if I spawn another continuation tree while parsing the same file? ...problems like these come to mind. Interesting stuff!

Native file locks in java

Java 1.4 introduced the native io layer into the JDK. One of the nice things you can do with it is to execute a native file lock that gets acknowledged by both “fcntl”- and “flock”-style locking. This is tremendously helpful if you need to share resources with native programs. So what is in C


  int fd = open("/path/to/file", O_RDWR);

  if (flock(fd,LOCK_EX) != 0 ) { ... }

  printf("locked file\\npress return");
  char c = getchar();

  if (flock(fd,LOCK_UN) != 0 ) { ... }

  printf("released file\\n");
  close(fd);


  int fd = open("/path/to/file", O_RDWR);

  struct flock lock;
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  lock.l_pid = 0;

  if (fcntl(fd, F_SETLK, &lock) == -1) { ... }

  printf("locked file\\npress return");
  char c = getchar();

  lock.l_type = F_UNLCK;

  if (fcntl(fd, F_SETLK, &lock) == -1) { ... }

  printf("released file\\n");
  close(fd);

becomes this in java


File file = new File("/path/to/file");
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
FileLock lock = channel.lock();

System.out.println("locked file\\npress return");
System.in.read();

lock.release();
System.out.println("released file\\n");

gpg-agent on OSX

When I had to sign my last pile of GnuPG keys I’ve investigated a bit how to ease this process a bit. I was looking for something like the ssh-agent / SSHKeyChain for OSX. Well, there is the gpg-agent but it was a bit tricky to get it working on OSX properly. First I’ve created a logon hook pointing to a “/etc/logon” file.


  sudo defaults write com.apple.loginwindow LoginHook /etc/logon

The file itself contains commands that supposed to get executed when a user logs in. The plan is to start the gpg-agent from there. One caveat though – the logon hook gets executed as root so the gpg-agent needs to get executed as the real user. The following file works for me just fine:


 #!/bin/sh
 su -l $1 -c "/sw/bin/gpg-agent --daemon --use-standard-socket" > \\
    /Users/$1/.gnupg/.gpg-agent

(I would be grateful if someone could explain why it does not work with “sudo” but only with “su”.) In order to have “gpg” use the agent an environment variable has to be set. Open or create a “~/.MacOSX/environment.plist” and define the GPG_AGENT_INFO in there.


 GPG_AGENT_INFO = /Users/tcurdt/.gnupg/S.gpg-agent:4559:1

So my file looks like this


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CVS_RSH</key>
    <string>ssh</string>
    <key>GPG_AGENT_INFO</key>
    <string>/Users/tcurdt/.gnupg/S.gpg-agent:4559:1</string>
    <key>SSH_AUTH_SOCK</key>
    <string>/tmp/501/SSHKeychain.socket</string>
</dict>
</plist>

So when you now use “gpg” it will once call out to the “pinentry” program defined in

".gnupg/gpg-agent.conf"

and then cache your passphrase.


 pinentry-program /sw/bin/pinentry
 no-grab
 default-cache-ttl 1800

So far pinentry only supports the gtk, qt or curses interface. In order to have a proper MacOSX integration I wrote a Carbon one. It works fine from the commandline. Unfortunately as it stands it still fails to load from the gpg-agent. (see the recent post about my execvp and Carbon problem)

Need a dedicated server?

We currently have at least 2 free slots/accounts on our dedicated server. It’s a machine in Germany similar to this offer. We pretty much just use if to have the freedom and flexibility to play. Subversion, weblogs, smtp server, webdav, java and ruby hosting …things like that. We are running Debian stable + backports currently, but I am also tempted to switch to Ubuntu (…but that’s to be discussed). Depending on how many people we find you should have at least around 10GB space and full root access. Rent is 45 EUR devided by the number of people. 2-3 more people would be nice so it would not be more than 10 EUR a month. If you known me or Marcus that would be a big plus.

Please contact one of us vie email if you are interested.

Cannot execvp a Carbon application

Here is a little test program to demonstrate a problem I am facing at the moment. I have an application (that I cannot change) that is calling out to execvp to execute an external program. Now I want to replace this external program by a Carbon application. Unfortunately for some reason the Carbon runtime cannot find the nib file (kIBCarbonRuntimeCantFindNibFile) It only succeeds if the this little test wrapper is inside the bundle and very next to the myapp executable.


#include "stdio.h"
int main(int argc, char** argv) {
  char* exe = "carbon/build/Release/myapp.app/Contents/MacOS/myapp";
  char* args[] = { "", NULL };

  printf("executing [%s]\n", exe);
  execvp (exe, args);
  printf("done\\n");
}

As execvp replaces the original program I just assume the nib will get searched based on the path of the wrapper – not the program that is getting called by execvp. Which I still think is weird, but anyway. If someone has further details, could confirm or explain …maybe even has a smart way around this – please let me know!!

UPDATE:
the above test program has a bug and “unfortunately” works and does not reproduce the error. Now that is even worse :-( …but just for the record it should have been.


#include "stdio.h"
int main(int argc, char** argv) {
  char* exe = "carbon/build/Release/myapp.app/Contents/MacOS/myapp";
  char* args[] = { exe, NULL };

  printf("executing [%s]\n", exe);
  execvp (args[0], args);
  printf("done\\n");
}

Reading the man page in detail sometimes *does* help.