简体   繁体   中英

How do I use one Java program, to monitor another Java program's output?

Below is diagram that shows what I'm trying to do : it is just 2 programs. One is a simple Child program that writes out integers every 2 seconds, line-by-line .

The other is a Parent program that monitors the log file ( just a very basic text file). If the log file doesn't get modified within 5 seconds, then it should restart the Child program (via a batch file ); then continue normally.

在此处输入图片说明

My code for the child class is here:

package fileiotestapplication;
import java.io.*;
import java.io.IOException;
import java.util.*;


public class WriterClass {

    @SuppressWarnings("oracle.jdeveloper.java.insufficient-catch-block")
    public WriterClass() {
        super();


            int[] content = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,};
            String[] friends = {"bob",};

                File file = new File("/C:/Java_Scratch/someFile.txt");
                // if file does not exists, then create it

            try {

                if (!file.exists()) {
                    file.createNewFile();
                }


                for (int i = 0 ; i < content.length; i++) 
                {                   

                        PrintStream bw = new PrintStream( new FileOutputStream(file, true) );     

                        System.out.println("testing " + i);
                        bw.println( String.valueOf(content[i]) );
                        bw.close();


                        Thread.sleep(2500);
                }


                System.out.println("Done");
            } catch (IOException ioe) {
                // TODO: Add catch code
                ioe.printStackTrace();
            }
            catch (InterruptedException ioe) {
                            // TODO: Add catch code
                            ioe.printStackTrace();
                }

        //someIS.println(i);
        System.out.println("This is OK");



    }

    public static void main(String[] args) {
        WriterClass writerClass = new WriterClass();


    }
}

The source code

And I linked here my current code for the Parent class .

What I'm now trying to do is add in some logic that catches when the child class stops writing output. What I'd like to do is count all the lines in the log file; and then compare them every 5 seconds, is this a good way (the alternative would be - to keep checking to see if the file got modified at all)?

EDIT: The suggestion below to use waitFor() indeed helps, though I'm still working out details : it is generally like :

  try {
   /* StackOverflow code */  

   for (  ;  ; )  {
   ProcessBuilder pb = new ProcessBuilder("TheBatchFile.bat");
   pb.directory(new File("C://Java_Scratch_//Autonomic_Using_Batch//"));
   Process p = pb.start();
   p.waitFor();
}
  /* end - StackOverflow code */  

  }
  catch (IOException i) {
    i.printStackTrace();
  }


  catch (InterruptedException i) {
    i.printStackTrace();
  }

This will get very slow as the file keeps growing in size. A simpler way would be to simply check the last modification time of the file. Assuming that the reason the child program might stop writing to the file is that the program terminates (rather than eg hanging in an infinite loop), it is probably better to directly monitor the child process itself rather than relying on observing the effects of the process. This is particularly convenient if the parent process can be responsible for starting the program in the first place.

This can be done with the ProcessBuilder and Process classes in Java 8. Copying from the documentation, you can start the process like this (if you only want to monitor whether it's running or not):

ProcessBuilder pb = new ProcessBuilder("TheBatchFile.bat", "Argument1", "Argument2");
pb.directory(new File("/path/to/working/dir"));
Process p = pb.start();

Then, you can simply call p.waitFor(); to wait for the process to terminate. Do this in a loop, and you have your automatic-restarting-of-child behavior.

You can use the directory watch service:

https://docs.oracle.com/javase/tutorial/essential/io/notification.html

You can configure a path or a file and register a watcher .

The watcher gets a notification every time a file is changed. You can store this timestamp of a notification for later use.

For details see my link above.

You may then use a Timer or a Thread to check last modification.

While your method of creating a text file, and using a batch script is feasible, there is a better way to approach it. This is a standard problem to approach with multitasking, and by creating a couple threads, it is not too difficult at all.

Using threads has several advantages over going externally "around" the system with batch files and multiple programs. For starters, these may include:

  1. Keeping everything together makes the project much tidier, cleaner, and marginally easier to distribute.

  2. It is easier to implement. Sure threads may seem confusing if you have never used them, but they are the lesser evil in my opinion, then all the steps involved in going around them. As I hope to show below, implementing this problem with threads is not hard.

  3. Improved performance, as the very expensive operations of file IO, and spawning the batch file are avoided. Threads also have improved performance over processes in most cases because they are easier to spawn, and multithreading sees performance improvements on a wider range of processors than multiprocessing by being less reliant on having several cores.

  4. No sketchy overlap between when one program is reading the file, while the other is writing to it simultaneously. These kind of situations are best avoided when possible.

  5. Maintains Java's impressive cross platform abilities, because you are not using batch which is not cross platform. This might not be important to you for this project, but you may come across something in the future with a similar problem, where this is more important, and so you will have practice implementing it.

  6. You learn better by using threads the "right way" instead of developing bad habits by using a more hacky approach. If this is a learning project, you might as well learn it right.

I went ahead and coded up the approach that I would most likely use to solve the problem. My code has a child thread the counts every two seconds, and a parent thread that monitors the child, and restarts it if the child goes five seconds without counting. Let's examine my program to give you a good idea of how it is working.

First, here is the class for the parent:

public class Parent {

    private Child child;

    public Parent(){
        child = new Child(this);
        child.start();
    }

    public void report(int count){ //Starts a new watchdog timer
        Watchdog restartTimer = new Watchdog(this, count);
        restartTimer.start();
    }

    public void restartChild(int currentCount){
        if (currentCount == child.getCount()){ //Check if the count has not changed
            //If it hasn't
            child.kill();
            child.start();
        }

    }


    public static void main(String[] args){
        //Start up the parent function, it spawns the child
        new Parent();
    }

}

The main function in there can be put somewhere else if you want, but to start everything up, just instantiate a parent. The parent class has an instance of the child class, and it starts up the child thread. The child will report it's counting to the parent with the report method, which spawns a watchdog timer (more on that in a second) that will call restartChild after five seconds with the current count. RestartChild, restarts the child thread, if the count is still the same as the one provided.

Here is the class for the watchdog timer:

class Watchdog implements Runnable { //A timer that will run after five seconds
       private Thread t;
       private Parent parent;
       private int initialCount;

       public Watchdog(Parent parent, int count){ //make a  timer with a count, and access to the parent
           initialCount = count;
           this.parent = parent;
       }

       public void run() { //Timers logic
           try {
               Thread.sleep(5000); // If you want to change the time requirement, modify it here
               parent.restartChild(initialCount);

           } catch (InterruptedException e) {
                System.out.println("Error in watchdog thread");
            }

       }

       public void start () // start the timer
       {
          if (t == null)
          {
             t = new Thread (this);
             t.start ();
          }
       }

    }

This watchdog timer is a thread that the parent will run with the start method. The parent sends itself as a parameter so that we can call the restartChild function of the parent.It stores the count, because when it runs after five seconds, restartChild will check if the count has changed.

And finally, here is the child class

public class Child implements Runnable{

    private Thread t;
    public int counter = 0;
    private boolean running;

    private Parent parent; // Record the parent function

    public Child(Parent parent){
        this.parent = parent;
    }

    private void initializeAll(){
        counter = 0;
        running = true;
    }

    public int getCount(){
        return counter;
    }

    @Override
    public void run() {
        while((counter <= 100)&&(running)){ 
            //The main logic for child
            counter +=1;
            System.out.println(counter);
            parent.report(counter); // Report a new count every two seconds

            try {
                Thread.sleep(2000); // Wait two seconds
            } catch (InterruptedException e) {
                System.out.println("Thread Failed");
            }
        }

    }

    public void start(){ //Start the thread

        initializeAll();
        t = new Thread(this);
        t.start();

    }

    public void kill(){ //Kill the thread
        running = false;
    }


}

This is also a thread, thus it implements runnable, and in that regard acts a lot like the watchdog. Run() is the main method of the child thread, this is where your logic goes that gets called when you start it. Starting the child with start() sets all the variables to their defaults, and then begins the run() logic. The logic in run is wrapped in if(running), because that lets us kill the thread internally by setting running to false.

Currently, all the child does right now is increment it's counter, output it to console, and then report the activity to the parent, 100 times, every two seconds. You will likely want to remove the condition stopping it after count passes 100, but I included it, so that the parent would eventual have cause to restart the child. To change the behavior, look at the child's run method, that is where all the main action is at.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM