All Downloads are FREE. Search and download functionalities are using the official Maven repository.

wetheinter.net.dev.gui.ProcessLog Maven / Gradle / Ivy

There is a newer version: 0.5
Show newest version
package wetheinter.net.dev.gui;

import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.WeakHashMap;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class ProcessLog extends Box {

  final JTextArea text;
  final JScrollPane scroller;
  StringBuffer body = new StringBuffer();

public ProcessLog() {
    super(BoxLayout.Y_AXIS);

    text = new JTextArea() {
      @Override
      public boolean getScrollableTracksViewportHeight() {
        return false;
      }

      @Override
      public boolean getScrollableTracksViewportWidth() {
        return false;
      }
    };
    scroller = new JScrollPane(text);
    add(scroller);
    out("Process X_Log");
    // setBounds(new Rectangle(500, 500));
    // set
    // setMaximumSize(new Dimension(500, 2000));
    // text.setMaximumSize(new Dimension(500, 2000));
  }

  HashMap onDone = new HashMap();
  WeakHashMap waitThreads = new WeakHashMap();
  ArrayList readerStd = new ArrayList();
  ArrayList readerErr = new ArrayList();
  WeakReference bufferReader;

  public synchronized void monitor(final Process handle, final String module) {
    try {
      int state = handle.exitValue();
      out("Process for module "+module+" has already terminated w/ exit code "+state);
      return;
    }catch(IllegalThreadStateException e) {
      //still running
      out("Process for module "+module+" is running; engaging logger...");
    }
    final BufferedReader in = new BufferedReader(new InputStreamReader(handle.getInputStream()))
    , err = new BufferedReader(new InputStreamReader(handle.getErrorStream()));
    readerStd.add(in);
    readerErr.add(err);
    if (onDone.containsKey(module)) {
      onDone.get(module).run();
    }
    onDone.put(module, new Runnable() {
      @Override
      public void run() {
        synchronized(ProcessLog.this) {
          readerStd.remove(in);
          readerErr.remove(err);
        }
        try {
          handle.destroy();
        } catch (Exception e) {
          processStdErr(handle, "Error destroying handle; " + e);
        }
      }
    });

    if (null == bufferReader || null == bufferReader.get() || bufferReader.isEnqueued()) {
      Runnable bufferRunnable = new Runnable() {
        @Override
        public void run() {
          try {
            while (isRunning()) {
              try {
                BufferedReader[] readers;
                synchronized(ProcessLog.this) {
                  readers = readerStd.toArray(new BufferedReader[readerStd.size()]);
                }
                for (BufferedReader read : readers) {
                  while (read.ready()) {
                    while (read.ready()) {
                      String next = read.readLine();
                      processStdIn(handle, next);
                    }
                    Thread.sleep(1);
                  }
                }
                synchronized(ProcessLog.this) {
                  readers = readerErr.toArray(new BufferedReader[readerErr.size()]);
                }
                for (BufferedReader read : readers) {
                  while (read.ready()) {
                    String next = read.readLine();
                    processStdErr(handle, next);
                  }
                }
                Thread.sleep(50);
              } catch (IOException e) {
                processStdErr(handle, "IOException @ " + Arrays.asList(e.getStackTrace()));
                return;
              } catch (InterruptedException e) {
                bufferReader = null;
                processStdErr(handle, "Interrupted @ " + Arrays.asList(e.getStackTrace()));
                return;
              }

            }
          } finally {
            bufferReader = null;
          }
        }
      };
      Thread ioblocker = new Thread(bufferRunnable);
      ioblocker.start();
      bufferReader = new WeakReference(ioblocker);
    }
    Thread waitThread = new Thread(new Runnable() {
      Thread selfRemove = new Thread(new Runnable() {

        @Override
        public void run() {
          out("Destroying module "+module);
          handle.destroy();// make sure our jvm kills any processes we started
        }
      });
      {
        Runtime.getRuntime().addShutdownHook(selfRemove);
      }

      @Override
      public void run() {
        try {
          try {
            int state = handle.exitValue();
            out("Process for module "+module+" has terminated w/ exit code "+state);
            return;
          }catch(IllegalThreadStateException e) {
            //still running
            out("Waiting for process "+module+" to finish...");
          }
          int result = handle.waitFor();
          out("Process "+module+" finished with exit code "+result);
          processCompletion(handle, result);
        } catch (InterruptedException e) {
          processStdErr(handle,
            "Interrupted while blocking on monitored command.  \n" + Arrays.asList(e.getStackTrace()));
          Thread.currentThread().interrupt();
        } finally {
          try {
            if (onDone.containsKey(module)) {
              onDone.remove(module).run();
            }
            if (null != selfRemove) Runtime.getRuntime().removeShutdownHook(selfRemove);
            selfRemove = null;
          } catch (Exception e) {
            e.printStackTrace();
            processStdErr(handle,
              "Error " + e + " while cleaning up process monitor.  \n" + Arrays.asList(e.getStackTrace()));
          }
        }
      }
    });
    waitThread.start();
    waitThreads.put(module, waitThread);

  }

  protected boolean isRunning() {
    synchronized(this) {
      return readerStd.size() > 0 || readerErr.size() > 0;
    }
  }

  protected void processCompletion(Process handle, int result) {

  }

  protected void processStdErr(Process handle, String next) {
    out("[ERROR] " + next);
  }

  private void out(String string) {
    body.append(string + "\n");
    scheduleRedraw();
    System.out.println(string);

  }

  public synchronized void stop(String module) {
    if (bufferReader != null) {
      Thread thread = bufferReader.get();
      bufferReader.clear();
      if (thread != null) thread.interrupt();
      bufferReader = null;
    }
    if (waitThreads.containsKey(module)) {
      waitThreads.remove(module).interrupt();
    }
    if (onDone.containsKey(module)) {
      onDone.remove(module).run();
    }
  }

  private Runnable redraw;

  private void scheduleRedraw() {
    if (redraw == null) {
      redraw = new Runnable() {
        final Rectangle visibleRect = scroller.getVisibleRect();
        private int lastHeight;

        @Override
        public void run() {
          redraw = null;
          boolean autoscroll = lastHeight == 0 || visibleRect.y + scroller.getHeight() == lastHeight;
          text.setText(body.toString());
          invalidate();
          if (autoscroll) {
            SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                lastHeight = visibleRect.y = scroller.getHeight() - visibleRect.height + 20;
                scroller.scrollRectToVisible(visibleRect);
              }
            });
          }
        }
      };
      SwingUtilities.invokeLater(redraw);
    }
  }

  protected void processStdIn(Process handle, String next) {
    out("[INFO] " + next);
  }

  public void log(String string, IOException e) {
    if (string != null && string.length() > 0) out(string);
    if (e != null) {
      out(e.toString());
      for (StackTraceElement el : e.getStackTrace()) {
        out(el.toString());
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy