1

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.

enter image description here

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();
  }
Caffeinated
  • 11,982
  • 40
  • 122
  • 216

3 Answers3

7

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 e.g. 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.

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • 3
    You can also see what the program is outputting. Just have the child program output to `System.out` instead of the file, and in the parent program, call `p.getOutputStream()` and read from it. (You can also use `p.getInputStream` to write to the child program's `System.in`). – James Westman Dec 16 '15 at 11:37
  • @kittycat3141 - I think I follow. Since both the parent and child are running in the same JVM correct? I'm guessing that `System.in` would be accessible to any java program running in the JVM ? – Caffeinated Dec 16 '15 at 16:16
  • 1
    @Coffee: That's not necessary - as a matter of fact, each running Java program has its own instance of the JVM; you _can't_ have multiple processes inside one JVM. The standard input and output streams are intended for communication _between_ processes. (You _can_ have multiple _threads_ in the same JVM, but then you might as well communicate with shared memory - all threads in the same process have access to the same variables.) – Aasmund Eldhuset Dec 16 '15 at 16:23
  • @AasmundEldhuset - I think I follow though it's a little confusing to me still. But we can have multiple programs running in same JVM ? [I was reading up here just now though where they say](http://stackoverflow.com/questions/16220700/is-it-possible-for-a-jvm-to-run-more-than-one-program-at-the-same-time) – Caffeinated Dec 16 '15 at 17:11
  • It took me a while, but I think I understand your answer now. I will give this a shot, I think I know how to code it. thanks ! – Caffeinated Dec 16 '15 at 21:46
  • @AasmundEldhuset - thanks so much , I finally got this code up & running. I actually am trying a new challenge now, [if you don't mind checking it out I'd greatly appreciate it](http://stackoverflow.com/questions/35518702/how-can-i-make-my-matrix-multiplication-java-code-more-fail-safe) - thanks ! – Caffeinated Feb 20 '16 at 20:43
  • 1
    @Coffee: Great! I was away for the weekend, and in the meantime, it looks like you got an extensive answer to your other question already. Good luck! – Aasmund Eldhuset Feb 22 '16 at 03:12
4

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.

Marcinek
  • 2,144
  • 1
  • 19
  • 25
  • Thanks ! Do you happen to have experience coding with the " directory watch service" ? I was curious about how it works – Caffeinated Dec 25 '15 at 19:22
  • 1
    The provided link shows an extensive example. If you have problems implementing it, consider post a question. – Marcinek Dec 26 '15 at 09:33
  • thanks so much , I finally got this code up & running. I actually am trying a new challenge now, [if you don't mind checking it out I'd greatly appreciate it](http://stackoverflow.com/questions/35518702/how-can-i-make-my-matrix-multiplication-java-code-more-fail-safe) - thanks ! – Caffeinated Feb 20 '16 at 20:44
2

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.

rp.beltran
  • 2,764
  • 3
  • 21
  • 29
  • Quick question - I can run this from command line, but as of now I'm not yet seeing how to kick off the Watchdog class. Or does that start once the Parent class gets kicked off ? thanks – Caffeinated Dec 17 '15 at 16:47
  • I can see though that using Threads is the preferred way to go , over the batch file – Caffeinated Dec 17 '15 at 16:48
  • 1
    I'm sorry I don't believe I fully understand your question. What do you mean by "kick off the watchdog class"? – rp.beltran Dec 17 '15 at 21:30
  • 1
    The timers are started when child reports it's count, they aren't killed when the child reports another number, but rather because the count changes, they have no effect. – rp.beltran Dec 17 '15 at 21:33
  • 1
    No worries, hopefully my last comment answered that, but just to clarify, Parent has the report function defined as `public void report(int count){Watchdog restartTimer = new Watchdog(this, count);restartTimer.start();}` This instantiates a new Watchdog, and starts it's timer with the start() function. This is called whenever the child reports a new action. I start it here, instead of when the last timer finishes so that it waits until five seconds after the last action, instead of just checking every five seconds like a clock, this way it stays caught up, instead of having a small tolerance. – rp.beltran Dec 17 '15 at 23:16
  • 1
    this is starting to clear up now. I'll keep at it, thanks very much ! – Caffeinated Dec 18 '15 at 02:44
  • - thanks again for your help. BTW, I actually am trying a new challenge now, [if you don't mind checking it out I'd greatly appreciate it](http://stackoverflow.com/questions/35518702/how-can-i-make-my-matrix-multiplication-java-code-more-fail-safe) - thanks ! – Caffeinated Feb 20 '16 at 20:44