users@glassfish.java.net

Re: Interrupting a running Servlet

From: <glassfish_at_javadesktop.org>
Date: Thu, 03 Apr 2008 20:49:53 PST

The beauty of UNIX like operating systems is that there's a zillion ways things can be done.

I'm not a fan of JNI for the simple reason that when things go wrong in pure Java, you basically tend to get an exception or something. When things go wrong with JNI, you tend to get a core dump which means a dead server.

I'm also not a big fan of forking the server to execute C programs, mostly because the servers tend to be LARGE and have an impact on the the system for the split second that the system is cloning the process to exec the new process. I've had large app servers fail to fork because there wasn't enough swap on the system to handle two copies of the system.

Mind, I don't see this application having that problem, but as a rule I resist forking the server if I can avoid it.

Which leads us to the next point: how can we trigger a remote process, and how can we do it easily?

Step one is to have a listener program, a daemon. But what does it listen to?

As was suggested, it could simply poll a directory looking for a file. That's simple and straightforward. It could use a socket. Open a socket, listen for traffic. This is trivial as well. inetd makes writing trivial socket servers, well, trivial. The detail is that a socket is a public resource -- anyone with the socket port number and able to route to it can talk to it. It's up to the application to judge whether the system sending data is authorized or not, and who wants to write that code? If this is inside behind your firewall, then this may be just fine. If it's not, then it gets more complicated and not worth the trouble.

On UNIX you have other sockets, they're called Unix Domain Sockets. These are actually limited to the system with the socket, they're not accessible on the network. Alas, you can't do this in Java, out of the box, Java has no support for Domain Sockets. (You can get a library that gives you this funcationality -- but again, there's the JNI stuff again.)

But, there's a 3rd, less common way to do this. And it's trivial as well.

On UNIX systems (Windows too, but they're different) there's a thing called a "Named Pipe". It's commonly referred to as a FIFO, First In First Out. Basically it's a little buffer in memory that two processes can use to talk with.

The beauty of it tho, specifically with Java, is that it operates just like a normal file, so Java (and anything else) can use them out of the box.

The way you use them is first you need to create one in the filesystem. This is done with the mkfifo command (it used to be the mknod command).
[code]
$ cd /tmp
$ mkfifo mypipe
[/code]

At this point, it's just like a normal file, so you need to make sure permissions and ownership are correct.

Now, your listener process simply opens the file for read. This can be a C program, a Perl program, or, golly, even a Java program.

[code]
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("/tmp/mypipe");
        BufferedReader br = new BufferedReader(fr);
        
        String line = br.readLine();
        while(line != null) {
            System.out.println(line);
            line = br.readLine();
        }
        br.close();
        fr.close();
    }
[/code]

Pretty simple. Take that code, plop it in a file, and run it. It'll just sit there. Then go to another terminal shell and type:
[code]
$ ls > /tmp/mypipe
[/code]

And, shazam, two things will happen. One, the list of files in your directory will be printed out by your Java process, and, two, the java process will end.

When the input side of a pipe is closed, the output side inevitably gets closed as well (after the data is run through of course). The actual pipe, /tmp/mypipe, remains though.

But there's another catch here. Try the experiment again, only this time run the 'ls' command first, THEN the Java process. You'll notice that the 'ls' command waits for someone to open up the other end, just like the Java process did when it was waiting for input.

So what does all of this mean?

It means that you have available a very simple IPC mechanism, and that it's reasonably reliable. If you know someone is reliably going to be on the other end of the pipe, you can safely, blindly open the pipe up and start read or writing data to it.

Here's what you can do.

[code]
#!/bin/sh
#
# brain dead daemon

# loop until the cows come home
while [ 1 ]
do
    # get the command -- no, really, this works
    cmd=`cat /tmp/mypipe`
    if [ $cmd = "start" ]
    then
        echo "starting program"
        # start the program somehow
    fi
    if [ $cmd = "stop" ]
    then
        echo "stopping program"
        # stop the program somehow
    fi
    if [ $cmd = "exit" ]
    then
        echo "exiting daemon"
        # exit the daemon
        exit;
    fi
done
[/code]

That shell script will run forever listening to that pipe to do your bidding. You can easily write this in C or anything else. Use the init.d scripts perhaps as inspiration, as they do this kind of thing all day long (start and stop servers).

In your Java code, open the pipe for output, (new FileOutputStream("/tmp/mypipe") or FileWriter("/tmp/mypipe")), print the command to it, and close it. Instant IPC.

Just make sure you synchronize your pipe writng code so only one thread talks to the pipe at a time.

The only downside to using simple code is that if for some reason the little daemon dies, the server will block waiting for the other end to open. There are all kinds of shenanigans and hocus pocus you can use to detect that, route around that, protect against that, but I'm betting your won't need them. Probably easier to have the daemon create and destroy the pipes when it starts up and shuts down, then the server will throw nasty (but readily detectable) File Not Found exceptions. There are other issues (just like with anything), but you really won't be running in to those for simple IPC like this.

If you want the CServer to talk back, start a thread, suck on a pipe (a different pipe, /tmp/myotherpipe), and use that to get commands back from the CServer to do what you will. Say the CServer can send back the file name it just created so your server can load it in to a DB or whatever.

The hi tech Java way of doing all this is to use JMS, JMS servers, clients, listeners, etc. Lot of code and complexity (lot of features too, but..). This is just a simpler model for a simpler task.

(Oh, I don't suggest /tmp for permanent pipes - lot of systems purge /tmp files on boot, or after they're a week old, and pipes are no exception...)

Good Luck...
[Message sent by forum member 'whartung' (whartung)]

http://forums.java.net/jive/thread.jspa?messageID=267680