views:

425

answers:

3

Is there a way to periodically run a Unix command (ps in my case) in Java? The loop I wrote:

while( this.check )
{
    try 
    {
            ProcessBuilder pb = new ProcessBuilder("ps");
            Process proc;

            System.out.println(" * * Running `ps` * * ");

            byte[] buffer;
            String input;

            proc = pb.start();
            BufferedInputStream osInput = 
                new BufferedInputStream(proc.getInputStream());

            //prints 0 every time after the first
            System.out.println(osInput.available());

            buffer = new byte[osInput.available()];
            osInput.read(buffer);
            input = new String(buffer);
            for( String line : input.split("\n"))
            {
                if( line.equals("") )
                    continue;
                this.handlePS(line);
            }

            proc.destroy();
            try 
            {
                Thread.sleep(10000);
            } 
            catch (InterruptedException ie) 
            {
                ie.printStackTrace();
            }
        } 
        catch (IOException ioe) 
        {
            ioe.printStackTrace();
        }
    }
}

doesn't work. It runs perfectly fine the first time, but there are 0 bytes available from the input stream every time after that. I'd try the watch command, but this Solaris box doesn't have that. I can't use a cron job since I need to know if the PID is there in the Java Application. Any ideas?

Thanks in advance.

EDIT: cannot use cron job

EDIT: I'm making a new Thread of the same type (PS) after it concludes, so I am definitely making a new ProcessBuilder every time.

EDIT: I put the loop that didnt work back in since it caused confusion.

+3  A: 

I'm not certain where the loop is, but you will need to create a new Proc object (and thus a new InputStream) each time through the loop. Otherwise you will always be looking at the result to the first call. The javadocs for ProcessBuilder indicate that you do not need to create one of those each time.

There may also be a race condition where the input stream is not yet ready when you callk available(). You should look at making certain that the input stream has reached EOF (which will happen with ps, although not with, say, top) before printing the results.

You are also not handling encoding properly, although I don't know what kind of encoding the output of "ps" is (outside of ASCII). Since "ps" is probably ASCII this is reasonably safe, but may not be for other commands (and for other input streams).

Kathy Van Stone
yeah, i'm making a new Process each time. did you read the code?
geowa4
put the loop that doesnt work back in to avoid confusion
geowa4
The code did not have a clear loop (before editing), given that it was not clear what the method boundaries were
Kathy Van Stone
new PS().start()?
geowa4
The race is between pb.start() and osInput.available(). The sleep command is not between those two calls. Process.waitFor() should be added inbetween, as noted by others. Even so, I tend not to trust available() when the goal is to get the entire stream contents.
Kathy Van Stone
now that's very helpful, thank you. edit your answer with that and you get the accepted.
geowa4
+1  A: 

In addition to Kathy's answer, you should also gather stdout and stderr in separate threads for each invocation. Otherwise the process will block waiting for you to read this data.

See this answer for more details.

EDIT. Are you calling waitFor() to gather any exit status ? The way I would normally approach this is to execute and then call waitFor(). I think destroy() may be redundant in this context.

Brian Agnew
got it. thanks for the tip! I actually called waitFor() right after I started the process
geowa4
+1  A: 

So I think that the problem is that you are checking the input stream before the end of ps execution. Try adding:

proc.waitFor()

before calling osInput.available().

Here is how I would have implemented it:

    TimerTask task = new TimerTask() {

        private void work() throws Exception {
            System.out.println("Now");
            ProcessBuilder pb = new ProcessBuilder("ps");
            Process p = pb.start();
            p.waitFor();
            BufferedReader reader
                = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                // Process line
                System.out.println(line);
            }
        }

        @Override
        public void run() {
            try {
                work();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };

    Timer timer = new Timer();
    long period = 5000;
    timer.scheduleAtFixedRate(task, 0, period);
notnoop