如何使用一个Java程序来监视另一个Java程序的输出?

时间:2015-11-17 16:49:51

标签: java multithreading io processbuilder

下面的图表显示了我尝试做的事情:它只是2个程序。一个是简单的Child程序,它每2秒逐行写出整数。

另一个是监视日志文件的Parent程序(只是一个非常基本的文本文件)。如果日志文件在5秒内没有被修改,那么它应该重新启动Child程序(通过批处理文件);然后继续正常。

enter image description 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

I linked here my current code for the Parent class

我现在要做的是添加一些在子类停止写输出时捕获的逻辑。我想要做的是计算日志文件中的所有行;然后每5秒对它们进行一次比较,这是一个好方法(另一种方法是 - 继续检查以查看文件是否完全被修改)?

编辑:以下使用waitFor()的建议确实有帮助,尽管我仍然在制定细节:它通常是:

  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();
  }

3 个答案:

答案 0 :(得分:7)

随着文件大小的增加,这将变得非常慢。一种更简单的方法是简单地检查文件的最后修改时间。假设子程序可能停止写入文件的原因是程序终止(而不是例如挂在无限循环中),最好直接监视子进程本身而不是依赖观察过程的影响。如果父进程可以首先负责启动程序,这将特别方便。

这可以通过Java 8中的ProcessBuilderProcess类来完成。从文档中复制,您可以像这样启动过程(如果您只想监视它是否正在运行) :

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

然后,您只需调用p.waitFor();即可等待进程终止。在循环中执行此操作,您将自动重新启动子行为。

答案 1 :(得分:4)

您可以使用目录监视服务:

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

您可以配置路径或文件并注册watcher

观察者每次更改文件时都会收到通知。您可以存储此通知的时间戳以供以后使用。

有关详细信息,请参阅上面的链接。

然后,您可以使用计时器或线程来检查上次修改。

答案 2 :(得分:2)

虽然您创建文本文件和使用批处理脚本的方法是可行的,但有一种更好的方法可以解决它。这是多任务处理的标准问题,通过创建几个线程,它根本不是太难。

使用线程比使用批处理文件和多个程序在系统外部“周围”有几个优点。首先,这些可能包括:

  1. 将所有东西放在一起使项目更加整洁,清洁, 并且稍微容易分发。

  2. 实施起来比较容易。如果你从来没有使用它们,肯定的线程可能会让人感到困惑,但在我看来它们是较小的邪恶,然后是绕过它们的所有步骤。正如我希望在下面展示的那样,用线程实现这个问题并不难。

  3. 改进了性能,因为文件IO的操作非常昂贵,并且避免了产生批处理文件。在大多数情况下,线程也比流程提高了性能,因为它们更容易产生,而且多线程可以看到比多处理更广泛的处理器上的性能改进,因为它不再依赖于多个核心。

  4. 当一个程序正在读取文件而另一个程序同时写入文件时,之间没有粗略的重叠。在可能的情况下,最好避免这种情况。

  5. 维护Java令人印象深刻的跨平台能力,因为您没有使用非跨平台的批处理。对于这个项目来说,这对你来说可能并不重要,但是你可能会在未来遇到类似的问题,这一点更为重要,因此你将练习实现它。

  6. 通过“正确的方式”而不是使用线程,您可以更好地学习     通过使用更黑客的方法养成坏习惯。如果这是一个     学习项目,你也可以正确地学习它。

  7. 我继续编写了我最有可能用来解决问题的方法。我的代码有一个子线程,每两秒计数一次,以及一个监视子进程的父线程,如果子进程没有计数,则重新启动它。让我们检查一下我的程序,让你很好地了解它是如何工作的。

    首先,这是父级的课程:

    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();
        }
    
    }
    

    如果你想要的话,那里的主要功能可以放在其他地方,但要开始一切,只需实例化一个父。父类具有子类的实例,并启动子线程。孩子将使用报告方法向父母报告它正在计数,该方法产生一个看门狗定时器(在一秒钟内更多)将在五秒后用当前计数调用restartChild。如果计数仍然与提供的计数相同,则重新启动子线程。

    以下是看门狗定时器的类:

    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 ();
              }
           }
    
        }
    

    此监视程序计时器是父计算机将使用start方法运行的线程。父节点将自身作为参数发送,以便我们可以调用parent的restartChild函数。它存储计数,因为当它在五秒后运行时,restartChild将检查计数是否已更改。

    最后,这是儿童班

    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;
        }
    
    
    }
    

    这也是一个线程,因此它实现了runnable,并且在这方面的行为很像看门狗。 Run()是子线程的主要方法,这是您启动它时调用的逻辑。使用start()启动子项将所有变量设置为其默认值,然后开始运行run()逻辑。运行中的逻辑包含在if(running)中,因为它允许我们通过将运行设置为false来内部杀死线程。

    目前,所有孩子现在正在增加它的计数器,将其输出到控制台,然后每两秒向父母报告100次活动。您可能希望在计数超过100后删除阻止它的条件,但我将其包括在内,以便父级最终可能会重新启动子级。要更改行为,请查看子项的run方法,即所有主要操作所在的位置。