Link to home
Start Free TrialLog in
Avatar of dirku
dirkuFlag for Germany

asked on

Waiting in main until a thread finishes.

I start a thread from my main class (which is a GUI/JFrame) to read a text file and doing some work on the read in data. How can I pause the main program until the thread finishes its work and returns?
I tried wait()/notify but I alway get a java.lang.IllegalMonitorStateException: current thread not owner
 exception.

I even tried to join the working thread: mainClass.join(); but then my app freezes.

Regards

Dirk
Berlin, Germany
Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

That should be

workerThread.join();

where workerThread is the text file reading thread
Avatar of dirku

ASKER

Sorry. My fault. Of course, I meant <thread>.join();
In this case the main (thread) doesn't stop. Oh no, wait() ;-)
--> The file was just so small. But I encountered another problem. The read in data shall be placed onto a JTextPane but the JTextPane is displayed only when I choose another file (from a JFilechooser) to read in. I already have a myTextPane.revalidate() and myTextPane.repaint() invocation at the end of the method. Why is the JTextPane not displayed immediately?

It looks 'funny': My JProgressBar shows the progress of the reading and grouping of the read in data (for instance of a huge file) but displays the data previously read in in the JTextPane. So the user will be confused, definitely. What can I do?
Avatar of Webstorm
Webstorm

Hi dirku,

wait & notify can only be used in synchronized contexts :

  synchronized(obj)
  { obj.wait(); }

  synchronized(obj)
  { obj.notify(); } // 1 suspended thread resumed

  synchronized(obj)
  { obj.notifyAll(); } // all suspended threads resumed

If you're calling join in an event handler, then don't - this is tantamount to not using another thread at all and will cause gui problems.

I would read the file into a StringBuffer then display at the end
Avatar of dirku

ASKER

I solved the synchronization problem. Now I am using wait(100); within a while loop which is checking a particular condition (the parser thread has to return an array to the main class) and the main program now does wait(100); while this array is null. After the thread has set the array this loop can be quit. The main program stopped until now because the thread never had the chance to do its work, I think.

However, I still have the problem that the JTextPane is not displayed until the second read of a file. More precisely, I extended JTextPane to my class TraceGroup which consists of a StringBuffer and an ID.
The StringBuffer keeps the lines of the read in text file grouped by a particular criterion (for instance a process id).
Within the JFrame there is a main panel (JPanel) having a BoxLayout and I want to put each TraceGroup into a separate Box in a XAXIS manner.
Here's the code that creates the instances and places them onto the panel:
  /**
   * Creates as much Boxes as are {@link TraceGroup}s available and places them
   * onto the main panel of the GUI. A <code>VerticalStrut</code> of a fixec
   * size separates th eboxes from each other. For ergonomic reasons the
   * background colour of the Boxes alternate.
   */
  private void runTraceViewer()
  {
    System.out.println("Aktiviere TraceViewer...");
      if (traceGroups != null)
      {
        for (int i = 0; i < traceGroups.length; i++)
        {
          Box box = Box.createVerticalBox();
          box.setAlignmentY(BoxLayout.PAGE_AXIS);
          box.setBackground(Color.darkGray);

          TraceGroup traceGroup = traceGroups[i];
          TraceViewer tv = new TraceViewer(traceGroup);
          if (i % 2 == 0)
          {
            tv.setBackground(this.box1Color);
          }
          else
          {
            tv.setBackground(this.box2Color);
          }

          box.add(tv);
          mainPane.add(box);
          mainPane.add(Box.createVerticalStrut(5));

          //tv.revalidate();
          //tv.repaint();
        } //end for i
        mainPane.revalidate();
        mainPane.repaint();
      } //end if(traceGroups != null)
      else
      {
      System.out.println("traceGroups ist null.");
      }
  }//end runTraceViewer
  //----------------------------------------------------------------------------

Can I do this in the background (doublebuffering)? A second effect is that the boxes appear after each other which looks like the screen is flickering. Of course, I want the TraceGroups to appear at once.
Avatar of dirku

ASKER

Hi, Webstorm.

I think you're right. My program seems to work properly now but I get an exception when starting it (I oversaw this):
java.lang.IllegalMonitorStateException: current thread not owner

      at java.lang.Object.wait(Native Method)

      at advantage.utils.TraceAnalyzer$2.actionPerformed(TraceAnalyzer.java:275)

      at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1764)

And the line of code is the line when I call wait(100):
while (traceGroups == null)
            {
              //Give the TraceFileParser thread a chance to do its work and
              //return the traceGroup[].
              try
              {
                wait(100);
              }
              catch (InterruptedException ex)
              {
              }
            }//end while (traceGroups == null)

What can I do to avoid this exception? When I do the following the programm freezes:

while (traceGroups == null)
            {
              //Give the TraceFileParser thread a chance to do its work and
              //return the traceGroup[].
              try
              {
                 synchronized(this)
                {
                  wait(100);
                }
              }
              catch (InterruptedException ex)
              {
              }
            }//end while (traceGroups == null)
}
>> When I do the following the programm freezes

Only one thread can access the synchronized(this) block for  this
You may need more objects (1 per thread):

    synchronized(obj)
    {
        obj.wait(100);
    }

But if you want to suspend the current thread for 100 ms, you should use Thread.sleep() instead:

    Thread.sleep(100);

Avatar of dirku

ASKER

Well, the problem is that I have this 'architecture':
public class TraceAnalyzer extends JFrame{
...

public static void main(String[] args){
  TraceAnalyzer tracy = new TraceAnalyzer();
}
}//end class TraceAnalyzer

public class TraceFileParser extends Thread{
...
}//end class TraceFileParser

TraceAnalyzer instantiates a new TraceFileParser which reads a huge trace file, groups the traces and updates a JProgressBar in TraceAnalyzer. That's why I want the TraceAnalyzer to wait for 100ms within a while loop which checks whether the TraceFileParser has put its result in a variable of TraceAnalyzer. If not then wait further 100ms else quit the loop.
This, however, causes the IllegalMonitorStateException.
You can do this :

    synchronized(this)
    {
          while (traceGroups == null) wait(); // thread suspened until notifyAll is called
    }

And add the following method:

    public synchronized void setTraceGroup(   tg)
    {
          threadGroups = tg;
          if (g!=null) notifyAll(); // resume all suspended threads
    }

Avatar of dirku

ASKER

No, I still get the IllegalMonitorStateException.
Can it be that my class TraceAnalyzer needs to implement the Runnable interface or to be started this way:

public class TraceAnalyzer extends JFrame
{
  ...

  public static void main(String[] args)
  {
    TraceAnalyzer tracy = new TraceAnalyzer();
    Thread t = new Thread(tracy);
    t.start();
  }//end main
}//end class TraceAnalyzer

Isn't it possible to invoke wait(); from the main class (or at least a non-Thread/non-Runnable) object? I still cannot see how the main program flow (of a non-Runnable-implementing/non-Thread-extending class) can be stopped to wait for a started thread and then continue working with the resulst supplied by this thread.
What you told me so far is what I have already read (but it seems I didn't understand properly). Thus, I would be happy to grant the points to if you make me knowing in this topic.  8-)
>> I still get the IllegalMonitorStateException.
Can you post the stack trace, and the related part of the code ?

Avatar of dirku

ASKER

I will paste ALL involved classes here since they are not too large. (There is a little problem in laying out the components in the left split pane panel. I don't know why but when I add just JButtons they are layed out properly. As soon as I add the JList, the JButtons aren't aligned left anymore.) If you solve these problem I would increase points to... 150(?)

1. AdvantageConstants.java:
package advantage.utils;

public interface AdvantageConstants
{
  public static final String TIMESTAMP       = "TIMESTAMP";
  public static final String PROCESS_ID      = "PROCESS_ID";
  public static final String THREAD_ID       = "THREAD_ID";
  public static final String TRACE_TOPIC     = "TRACE_TOPIC";
  public static final String TRACE_LEVEL     = "TRACE_LEVEL";
  public static final String CFRAME_TRACETAG = "CFRAME_TRACETAG";
  public static final String CAT_TYPE_NAME   = "CAT_TYPE_NAME";
  public static final String CAT_INSTANCE_ID = "CAT_INSTANCE_ID";
  public static final String CAT_TYPE_ID     = "CAT_TYPE_ID";
  public static final String COMPONENT_ID    = "COMPONENT_ID";
  public static final String CONTEXT_ID      = "CONTEXT_ID";
  public static final String CONTEXT_TYPE_ID = "CONTEXT_TYPE_ID";
  public static final String TRACE_MESSAGE   = "TRACE_MESSAGE";
}//end interface AdvantageConstant

2. TraceGroup.java:
package advantage.utils;

import java.util.Comparator;


public class TraceGroup implements Comparator
{
  private int id = -1;
  private StringBuffer traces = null;
  //----------------------------------------------------------------------------

  public TraceGroup()
  {
  }//end default constructor
  //----------------------------------------------------------------------------

  public TraceGroup(int id)
  {
    this.id = id;
  }//end constructor
  //----------------------------------------------------------------------------

  public TraceGroup(int id, StringBuffer text)
  {
    this.id = id;
    this.traces = text;
  }//end constructor
  //----------------------------------------------------------------------------

  public void setId(int id)
  {
    this.id = id;
  }//end setId
  //----------------------------------------------------------------------------

  public int getId()
  {
    return this.id;
  }//end getId
  //----------------------------------------------------------------------------

  public void setTraces(StringBuffer text)
  {
    this.traces = text;
  }//end setTraces
  //----------------------------------------------------------------------------

  public StringBuffer getTraces()
  {
    return this.traces;
  }//end getTraces
  //----------------------------------------------------------------------------

  public int compare(Object object, Object object1)
  {
    return 0;
  }
  //----------------------------------------------------------------------------

  public boolean equals(Object object)
  {
    return false;
  }
  //----------------------------------------------------------------------------
}//end class TraceGroup

3. TraceFileParser.java:
package advantage.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

import javax.swing.SwingUtilities;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JProgressBar;

/**
 * <p>Title: TraceAnalyzer</p>
 * <p>Description: An analyzing tool for trace files.</p>
 * <p><br>This <code>TraceFileParser</code> takes a trace file which is conform
 * to the Siemens AG conventions.</p>
 * A trace con consist of the following parts although it is not mandatory that
 * each of these components are in a particular trace:</p>
 * <ul>
 *   <li>Timestamp (date and time with milliseconds)</li>
 *   <li>Process ID</li>
 *   <li>Thread ID</li>
 *   <li>Trace topic</li>
 *   <li>Trace level</li>
 *   <li>CFRAME trace tag</li>
 *   <li>container_name::CAT type name</li>
 *   <li>container_name::CAT instance ID</li>
 *   <li>CAT type ID</li>
 *   <li>Component ID</li>
 *   <li>Context ID</li>
 *   <li>Context type ID</li>
 *   <li>Trace message</li>
 * </ul>
 * <p>Copyright: Copyright (c) 2004 by du-it</p>
 * <p>Company: du-it Dirk Ulrich-Informationstechnologie</p>
 * @author Dirk Ulrich d.ulrich@du-it.de
 * @version 1.0
 */

//public class TraceFileParser implements Runnable
public class TraceFileParser extends Thread
{
  private File fileToParse = null;
  private TraceGroup[] traceGroups = null;
  private long parsedData = 0;
  private int groupedTraceGroups = 0;
  private String pattern = "";
  private String groupCriterion = "";

  private HashMap groupCriteria = new HashMap();
  private HashMap tracePool = null;
  private TraceAnalyzer analyzer = null;

  //Variables needed for running the thread.
  boolean done = false;
  boolean grouping = false;
  int value, currentValue;
  JProgressBar pb, comparePb;
  Runnable getValue, setValue;
  //----------------------------------------------------------------------------

  public TraceFileParser(TraceAnalyzer analyzer)
  {
    this(analyzer, null);
  }//end default constructor
  //----------------------------------------------------------------------------

  public TraceFileParser(TraceAnalyzer analyzer, File file)
  {
    super();
    this.analyzer = analyzer;
    this.fileToParse = file;
    groupCriteria.put(AdvantageConstants.TIMESTAMP, new Integer(0));
    groupCriteria.put(AdvantageConstants.PROCESS_ID, new Integer(1));
    groupCriteria.put(AdvantageConstants.THREAD_ID, new Integer(2));
    groupCriteria.put(AdvantageConstants.TRACE_TOPIC, new Integer(3));
    groupCriteria.put(AdvantageConstants.TRACE_LEVEL, new Integer(4));
    groupCriteria.put(AdvantageConstants.CFRAME_TRACETAG, new Integer(5));
    groupCriteria.put(AdvantageConstants.CAT_TYPE_NAME, new Integer(6));
    groupCriteria.put(AdvantageConstants.CAT_INSTANCE_ID, new Integer(7));
    groupCriteria.put(AdvantageConstants.CAT_TYPE_ID, new Integer(8));
    groupCriteria.put(AdvantageConstants.COMPONENT_ID, new Integer(9));
    groupCriteria.put(AdvantageConstants.CONTEXT_ID, new Integer(10));
    groupCriteria.put(AdvantageConstants.CONTEXT_TYPE_ID, new Integer(11));
    groupCriteria.put(AdvantageConstants.TRACE_MESSAGE, new Integer(12));
  }//end standard constructor
  //----------------------------------------------------------------------------

  /**
   * Sets the grouping criterion directly
   * @param criterion
   */
  public void setGroupCriterion(String criterion)
  {
    if(groupCriteria.containsKey(criterion))
    {
      this.groupCriterion = criterion;
    }
    else
    {
      //TODO: Throw an exception.
    }
  }//end setGroupCriterion
  //----------------------------------------------------------------------------

  /**
   * Determines the group criterion via the seelcted index in the
   * <code>JComboBox</code> in the TraceAnalyzer GUI.
   * @param criterionPosition - The selected index in the JComboBox which is the
   *                            position in the enumeration of the grouping
   *                            options.
   */
  public void setGroupCriterion(int criterionPosition)
  {
System.out.println("criterionPosition: " + criterionPosition);
    java.util.Set set = groupCriteria.keySet();
    for(java.util.Iterator it = set.iterator(); it.hasNext();)
    {
      Object token = it.next();
      Integer value = (Integer)groupCriteria.get(token);
      if(value.intValue() == criterionPosition)
      {
        this.groupCriterion = (String) token;
System.out.println("groupCriteria[" + criterionPosition + "] = " + token);
      }
    }
  }//end setGroupCriterion
  //----------------------------------------------------------------------------

  /**
   * Sets the pattern to be used when parsing the source file.
   * @param pattern The pattern to search for in the source file.
   */
  public void setPattern(String pattern)
  {
    this.pattern = pattern;
  }//end setPattern
  //----------------------------------------------------------------------------

  /**
   * Parses a file to find matches for the given pattern and updates an
   * associated <code>JProgressBar</code>.
   * @param pattern The pattern to look for within the file to parse.
   */
  public void run()
  {
        //BEGIN: Declaration and definition
        TraceGroup tGroup = new TraceGroup();
        StringBuffer sb = new StringBuffer();
        FileReader fr = null;
        BufferedReader bufr = null;
        tracePool = new HashMap();

        pb = analyzer.getFileloadProgressbar();
        comparePb = analyzer.getFileloadProgressbar();
        //END: Declaration and definition

        getValue = new Runnable()
    {
      public void run()
      {
        pb = analyzer.getFileloadProgressbar();
        currentValue = pb.getValue();
      }
    }
    ;

    setValue = new Runnable()
    {
      public void run()
      {
        comparePb = analyzer.getFileloadProgressbar();
        comparePb.setValue(value);
        comparePb.setString(value + " / " + comparePb.getMaximum());

        if (grouping) {
          if (TraceFileParser.this.getTraceGroups() != null) {
            comparePb.setString(comparePb.getString() + " Groups");
            if (comparePb.getValue() == comparePb.getMaximum())
              done = true;
          }
        }
        else {
          comparePb.setString(comparePb.getString() + " Bytes");
/*
System.out.println("comparePb.getString().length() = " + comparePb.getString().length() + " ----- comparePb.getWidth() = " + comparePb.getWidth());
System.out.println("comparePb.getWidth()-comparePb.getWidth()*50/100 = " + (comparePb.getWidth()-comparePb.getWidth()*50/100));
System.out.println("comparePb.getString().length()+comparePb.getString().length()*50/100 = " + (comparePb.getString().length()+comparePb.getString().length()*50/100));
*/
          //if((comparePb.getWidth()-comparePb.getWidth()*50/100) <= comparePb.getString().length())
          if((comparePb.getWidth()/2) <= comparePb.getString().length())
          {
            comparePb.setSize(comparePb.getWidth()+comparePb.getString().length()/2,
                              comparePb.getHeight());
          }
        }
      }
    };

    try {
      fr = new FileReader(fileToParse);
    }
    catch (FileNotFoundException fnfE) {}
    bufr = new BufferedReader(fr);
    String line = new String("");
    int i = 0; //traceGroup ID

    while (!done) {
      try {
        while ( (line = bufr.readLine()) != null) {
          parsedData += (line + "\n").length();
          value = new Long(parsedData).intValue();

          String groupCriterionValue = this.getGroupCriterionValue(line);
//System.out.println("groupCriterionValue: " + groupCriterionValue);
          if (!tracePool.containsKey(groupCriterionValue)) {
            sb = new StringBuffer(line);
            sb.append("\n");
            tGroup = new TraceGroup(i++, sb);
            tracePool.put(groupCriterionValue, tGroup);
          }
          else {
            tGroup = (TraceGroup) tracePool.get(groupCriterionValue);
            sb = tGroup.getTraces();
            sb.append(line);
            sb.append("\n");
            tracePool.put(groupCriterionValue, tGroup);
          }

          try {
            SwingUtilities.invokeAndWait(getValue);
            /*
            if (currentValue != value)
            {
              System.out.println("------------->if(currentValue != value)");
              SwingUtilities.invokeAndWait(setValue);
            }
            */
          }
          catch (InvocationTargetException itE) {
            itE.printStackTrace();
          }
          catch (InterruptedException iE) {
            //iE.printStackTrace();
            if(this.isInterrupted())
            {
              System.out.println("Tschüss!");
            }
          }

           if (currentValue != value) {
            SwingUtilities.invokeLater(setValue);
          }

        } //end while ((line = bufr.readLine()) != null)
        grouping = true;
        value = 1;
        analyzer.getFileloadProgresslabel().setText("Grouping traces...");
        analyzer.getFileloadProgressbar().setMaximum(tracePool.size());

        traceGroups = new TraceGroup[tracePool.size()];
        java.util.ArrayList al = new java.util.ArrayList(tracePool.values());
        for (int x = 0; x < al.size(); x++) {
          value = x + 1;
          try {
            SwingUtilities.invokeAndWait(getValue);
            /*
             if (currentValue != value)
              SwingUtilities.invokeAndWait(setValue);
            */
           sleep(10);
          }
          catch (InvocationTargetException itE) {
            itE.printStackTrace();
          }
          catch (InterruptedException iE) {
            iE.printStackTrace();
          }

           if (currentValue != value) {
            SwingUtilities.invokeLater(setValue);
          }

          System.out.println("Copying TraceGroup #" + x);
          //try{sleep(100);}catch(Exception ex){}
          traceGroups[x] = (TraceGroup) al.get(x);
          //this.groupedTraceGroups++;
        }
      }
      catch (IOException ex) {
      }
    } //end while (!done)
    comparePb.setString("Done!");
    System.out.println("Ciao bella.");
    /*analyzer.notifyMe();
  }*/
    analyzer.setTraceGroups(traceGroups);
  }//end run
  //----------------------------------------------------------------------------

  /**
   * Returns the value for a particular position from the trace file. This
   * position is determined by the Siemens conventions for writing trace files
   * and can be found using the set #groupCriterion.
   * @param line - The string to parse for a group criterion.
   * @return - The value for a group criterion.
   * @see #groupCriterion
   */
  protected String getGroupCriterionValue(String line)
  {
    StringTokenizer st = new StringTokenizer(line, "-", false);
    String token = "";
    int criterionPosition = ((Integer)groupCriteria.get(this.groupCriterion)).intValue();

    //Group only according to the date; not to the time. Since the delimiter
    //between the respective trace parts (process ID, thread ID,
    //component ID, ...) is equal to the delimiter to separate the parts of
    //a timestamp (DATE - TIME) we have to skip the TIME part.
    //Grouping on the TIME doesn't make sense since the milliseconds changes
    //continiously (what a surprise!) we would have as much TraceGroups as
    //we have traces in the trace file.
    if(criterionPosition > ((Integer)groupCriteria.get(AdvantageConstants.TIMESTAMP)).intValue())
    {
      criterionPosition++;
    }

    int tokenCounter = 0;
    try
    {
      while (st.hasMoreTokens())
      {
        token = st.nextToken();
//System.out.println("Das Kriterium hat den Wert: " + token);
        if(tokenCounter++ == criterionPosition)
        {
          break;
        }
      }
    }
    catch(NoSuchElementException nseE)
    {
    }
    return token;
  }//end getGroupCriterionValue
  //----------------------------------------------------------------------------

  /**
   * Safely stop a running thread by setting the run's loop condition to false.
   */
  public void safeStop()
  {
    this.done = true;
  }//end safeStop
  //----------------------------------------------------------------------------

  /**
   * Returns the temporary container keeping the already determined
   * {@link TraceGroup}s.
   * @return The {@link TraceGroup}s temporary stroed in a HashMap.
   */
  public synchronized HashMap getTracePool()
  {
    return tracePool;
  }//end getTracePool
  //----------------------------------------------------------------------------

  /**
   * Returns the file the <code>TraceFileParser</code> has to parse.
   * @return The trace file to parse.
   */
  public File getFileToParse()
  {
    return this.fileToParse;
  }//end getFileToParse
  //----------------------------------------------------------------------------

  /**
   * Returns the amount of data already read/parsed from the source file.
   * @return The amount of data already read/parsed from the source file.
   */
  public synchronized long getParsedData()
  {
    return this.parsedData;
  }//end getParsedData
  //----------------------------------------------------------------------------

  public int getGroupedTraceGroups()
  {
    return this.groupedTraceGroups;
  }//end get
  //----------------------------------------------------------------------------

  /**
   * Returns the determined groups a trace file contains.
   * @return traceGroups - An array of {@link TraceGroup} instances.
   */
  public TraceGroup[] getTraceGroups()
  {
    return this.traceGroups;
  }//end getTraceGroups
  //----------------------------------------------------------------------------
}//end class TraceFileParser

4. TraceAnalyer.java (main):
package advantage.utils;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;

import javax.swing.Box;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import java.lang.reflect.InvocationTargetException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import javax.swing.text.BadLocationException;

//import java.awt.HeadlessException;
import java.util.ResourceBundle;
import java.util.Locale;

/**
 * <p>Title: TraceAnalyzer</p>
 * <p>Description: An analyzing tool for trace files.</p>
 * <p><br>This TraceAnalyzer uses a {@link TraceFileParser} to group the traces
 * within a trace file. These grouped traces will then be displayed with an
 * alternating background color to identify the {@link TraceGroup}s easier.</p>
 * <p><br/>Since the trace files can be huge it is important to allocate
 * sufficient memory using this command line option: -Xmx<desiredRAMinMB>m
 * where <desiredRAMinMB> is the desired memory to allocate as an integer.
 * The subsequent 'm' defines that this number is in MB.
 * </p>
 * <p>Copyright: Copyright (c) 2004 by du-it</p>
 * <p>Company: du-it Dirk Ulrich-Informationstechnologie</p>
 * @author Dirk Ulrich d.ulrich@du-it.de
 * @version 1.0
 */

public class TraceAnalyzer extends JFrame
{
  private transient ResourceBundle rbundle = null;
  private transient TraceFileParser tfParser = null;
  private transient TraceGroup[] traceGroups = null;
  private Color bgColor = Color.gray;
  private Color box1Color = Color.white;
  private Color box2Color = Color.gray.brighter();

  private JSplitPane splitPane = null;
  private JPanel mainPane = new JPanel();
  private JPanel topPane = new JPanel();
  private JPanel leftNavPane = new JPanel();
  private JPanel statusPane = new JPanel();
  private JButton openFileButton = null;
  private JFileChooser fileChooser = null;
  private JList traceGroupJumperList = null;
  private JProgressBar fileloadProgressbar = null;
  private JLabel fileloadProgresslabel = null;
  private JComboBox groupCriterionChooser = null;

  private String openFileButtonStr = "";
  private String fileloadProgressStr = "";
  private String groupingProgressStr = "";
  private JButton b1 = null;
  private JButton b2 = null;
  //----------------------------------------------------------------------------

  /**
   * The <code>TraceAnalyzer</code> default constructor initializes the
   * application and make it visible.
   */
  public TraceAnalyzer() //throws HeadlessException
  {
    super();
    init();
    this.setVisible(true);
System.out.println("b1 = " + b1.getBounds());
System.out.println("b2 = " + b2.getBounds());
System.out.println("traceGroupJumperList = " + traceGroupJumperList.getBounds());
  }//end default constructor
  //----------------------------------------------------------------------------

  /**
   * Initializes the <code>TraceAnalyzer</code>.
   */
  private void init()
  {
    initConfiguration();
    initGuiComponents();
    addListeners();
  }//end init
  //----------------------------------------------------------------------------

  /**
   * Initializes this <code>TraceAnalyzer</code>'s configuration using underlying
   * property files/ResourceBundles.
   */
  private void initConfiguration()
  {
   try
   {
     rbundle = ResourceBundle.getBundle("traceAnalyzerConf");
     this.bgColor = Color.decode(rbundle.getString("TraceAnalyzerBgColor"));
     this.box1Color = Color.decode(rbundle.getString("TraceViewer1Color"));
     this.box2Color = Color.decode(rbundle.getString("TraceViewer2Color"));
     String locale_lang = rbundle.getString("locale_language");
     String locale_country = rbundle.getString("locale_country");
     Locale locale = new Locale(locale_lang, locale_country);
     rbundle = ResourceBundle.getBundle("traceAnalyzer", locale);
     fileloadProgressStr = rbundle.getString("fileloadingProgressLabel");
     groupingProgressStr = rbundle.getString("filegroupingProgressLabel");
     openFileButtonStr = rbundle.getString("openFileButtonLabel");
   }
   catch(Exception e)
   {
      e.printStackTrace();
   }
    int width = Integer.parseInt(rbundle.getString("width"));
    int height = Integer.parseInt(rbundle.getString("height"));

    this.setSize(width, height);
  }//end initConfiguration
  //----------------------------------------------------------------------------

  /**
   * Initializes the GUI components of the <code>TraceAnalyzer</code>.
   */
  private void initGuiComponents()
  {
    //The layout of the application itself.
    this.getContentPane().setLayout(new BorderLayout());

    //NORTH components
    openFileButton = new JButton(openFileButtonStr);

    //The sequence is significant and relates to the TraceFileParser's HashMap
    //to determine the position of the grouping criterion amongst the tokens of
    //a trace.
    groupCriterionChooser = new JComboBox(new String[]{
                                rbundle.getString("criteria_TIMESTAMP"),
                                rbundle.getString("criteria_PROCESS_ID"),
                                rbundle.getString("criteria_THREAD_ID"),
                                rbundle.getString("criteria_TRACE_TOPIC"),
                                rbundle.getString("criteria_TRACE_LEVEL"),
                                rbundle.getString("criteria_CFRAME_TRACETAG"),
                                rbundle.getString("criteria_CAT_TYPE_NAME"),
                                rbundle.getString("criteria_CAT_INSTANCE_ID"),
                                rbundle.getString("criteria_CAT_TYPE_ID"),
                                rbundle.getString("criteria_COMPONENT_ID"),
                                rbundle.getString("criteria_CONTEXT_ID"),
                                rbundle.getString("criteria_CONTEXT_TYPE_ID"),
                                rbundle.getString("criteria_TRACE_MESSAGE")});


    fileloadProgresslabel = new JLabel(fileloadProgressStr);
    fileloadProgressbar = new JProgressBar();
    fileloadProgressbar.setStringPainted(true);

    topPane.setLayout(new FlowLayout(FlowLayout.LEADING));
    topPane.add(openFileButton);
    topPane.add(fileloadProgresslabel);
    topPane.add(fileloadProgressbar);
    topPane.add(groupCriterionChooser);

    //CENTER components
    JScrollPane mainScrollPane = new JScrollPane(mainPane);

      //RIGHT JSplitPane components
      BoxLayout bl = new BoxLayout(mainPane, BoxLayout.PAGE_AXIS);
      mainPane.setLayout(bl);
      mainPane.setBackground(Color.orange);

      //LEFT JSplitPane components
      leftNavPane.setLayout(new BoxLayout(leftNavPane, BoxLayout.PAGE_AXIS));
      leftNavPane.setBorder(BorderFactory.createEmptyBorder(10,0,10,10));
      leftNavPane.setBackground(Color.green);

      b1 = new JButton("111");
      b1.setAlignmentX(LEFT_ALIGNMENT);
      leftNavPane.add(b1);
leftNavPane.add(Box.createRigidArea(new Dimension(0,5)));
      b2 = new JButton("222");
      b2.setAlignmentX(LEFT_ALIGNMENT);
      leftNavPane.add(b2);
leftNavPane.add(Box.createRigidArea(new Dimension(0,5)));
      traceGroupJumperList = new JList();
      traceGroupJumperList.setAlignmentY(LEFT_ALIGNMENT);
      leftNavPane.add(traceGroupJumperList);
      traceGroupJumperList.add(new JLabel("cc"));
      java.util.Vector v = new java.util.Vector();
      v.add(new String("aaa"));
      v.add(new String("zzz"));
      traceGroupJumperList.setListData(v);
      traceGroupJumperList.revalidate();
      traceGroupJumperList.repaint();

    splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                               leftNavPane, mainScrollPane);

    //SOUTH components
    statusPane = new JPanel();

    //Putting it altogether
    this.getContentPane().add(topPane, BorderLayout.PAGE_START);
    this.getContentPane().add(splitPane, BorderLayout.CENTER);
    this.getContentPane().add(statusPane, BorderLayout.SOUTH);
  }//end initGuiComponents
  //----------------------------------------------------------------------------

  /**
   *
   */
  private void initTraceGroups()
  {
    int v = new Long(tfParser.getFileToParse().length()).intValue();
    this.fileloadProgressbar.setMaximum(v);
    this.fileloadProgressbar.setMinimum(0);
    tfParser.setPattern("");
    tfParser.start();
  }//end initTraceGroups
  //----------------------------------------------------------------------------

  /**
   * Registers listeners to particular components to handle events.
   */
  private void addListeners()
  {
    //Handle the window closing event.
    addWindowListener(new java.awt.event.WindowAdapter()
    {
      public void windowClosing(java.awt.event.WindowEvent evt)
      {
        exitAnalyzer(evt);
      }
    });

    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    openFileButton.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent aE)
      {
        fileChooser = new JFileChooser();
        if (aE.getSource() == openFileButton) {
          int returnVal = fileChooser.showOpenDialog(TraceAnalyzer.this);
          if (returnVal == JFileChooser.APPROVE_OPTION)
          {
            File file = fileChooser.getSelectedFile();
            statusPane.removeAll();
            statusPane.add(new JLabel(file.getName()));
            statusPane.add(new JLabel(": "));
            statusPane.add(new JLabel(file.length() + " Byte"));
            statusPane.revalidate();
            statusPane.repaint();
            tfParser = new TraceFileParser(TraceAnalyzer.this, file);

            tfParser.setGroupCriterion(groupCriterionChooser.getSelectedIndex());
            initTraceGroups();

            if (mainPane.getComponentCount() > 0)
            {
              mainPane.removeAll();
            }
            while (traceGroups == null)
            {
              //Give the TraceFileParser thread a chance to do its work and
              //return the traceGroup[].
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!wait(100) causes an IllegalMonitorException!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
              try
              {
                wait(100);
              }
              catch (InterruptedException ex)
              {
              }
            }//end while (traceGroups == null)
            runTraceViewer();
          } //end if(returnVal == JFileChooser.APPROVE_OPTION)
        } //end if(e.getSource() == testButton)
      } //end actionPerformed
    });

    //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    groupCriterionChooser.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent aE)
      {

      }
    });
  } //end addListeners
  //----------------------------------------------------------------------------

  /**
   * Resets the GUI to its default state concerning labels etc.
   */
  private void resetGUI()
  {
  }//end resetGUI
  //----------------------------------------------------------------------------

  /**
   * Creates as much Boxes as are {@link TraceGroup}s available and places them
   * onto the main panel of the GUI. A <code>VerticalStrut</code> of a fixed
   * size separates th eboxes from each other. For ergonomic reasons the
   * background colour of the Boxes alternate.
   */
  private void runTraceViewer()
  {
      if (traceGroups != null)
      {
        for (int i = 0; i < traceGroups.length; i++)
        {
          Box box = Box.createVerticalBox();
          box.setAlignmentY(BoxLayout.PAGE_AXIS);
          box.setBackground(Color.darkGray);

          //TraceGroup[] tGroups = new TraceGroup[traceGroups.length];
          //System.arraycopy(traceGroups, 0, tGroups, 0, traceGroups.length);
//System.out.println("tGroups.length = " + tGroups.length);
//java.util.Arrays.sort(tGroups,0,tGroups.length);
          TraceGroup traceGroup = traceGroups[i];
          //TraceGroup traceGroup = tGroups[i];
          TraceViewer tv = new TraceViewer(traceGroup);
System.out.println("Ausgabe der TraceGroup ID #[" + traceGroup.getId() + "].");
          if (i % 2 == 0)
          {
            tv.setBackground(this.box1Color);
          }
          else
          {
            tv.setBackground(this.box2Color);
          }

          box.add(tv);
          mainPane.add(box);
          mainPane.add(Box.createVerticalStrut(5));
        } //end for i
        mainPane.revalidate();
        mainPane.repaint();
      } //end if(traceGroups != null)
      else
      {
      System.out.println("traceGroups ist null.");
      }
  }//end runTraceViewer
  //----------------------------------------------------------------------------

  /**
   * Sets the {@link TraceGroup}s to be displayed within this
   * <code>TraceAnalyzer</code>.
   * @param tGroups The {@link TraceGroup} array to be displayed.
   */
  public synchronized void setTraceGroups(TraceGroup[] tGroups)
  {
    this.traceGroups = tGroups;
    if(traceGroups != null)
      notifyAll();
  }//end setTraceGroups
  //----------------------------------------------------------------------------

  /**
   * Returns the {@link TraceGroup}s which are in fact the instantiated
   * {@link TraceViewer}s.
   * @return The {@link TraceGroup}s handled by this <code>TraceAnalyzer</code>.
   */
  public TraceGroup[] getTraceGroups()
  {
    return this.traceGroups;
  }//end getTraceGroups
  //----------------------------------------------------------------------------

  /**
   *
   * @return
   */
  public JLabel getFileloadProgresslabel()
  {
    return this.fileloadProgresslabel;
  }//end getFileloadProgressLabel
  //----------------------------------------------------------------------------

  /**
   *
   * @return
   */
  public JProgressBar getFileloadProgressbar()
  {
    return this.fileloadProgressbar;
  }//end getFileloadProgressbar
  //----------------------------------------------------------------------------

  /**
   * Guarantee a safe exit off this application.
   * @param evt
   */
  private void exitAnalyzer(java.awt.event.WindowEvent evt)
  {
    if(tfParser != null && tfParser.isAlive())
    {
      //tfParser.interrupt();
      tfParser.safeStop();
    }//end if(tfParser.isAlive())
    System.gc();
    System.exit(0);
  }//end exitAnalyzer
  //----------------------------------------------------------------------------

  /****************************************************************************/
  /**************************** INNER CLASSES *********************************/
  /****************************************************************************/
  //============================================================================
  public static void main(String[] args) //throws HeadlessException
  {
    TraceAnalyzer tracy = new TraceAnalyzer();
  }//end main

}//end class TraceAnalyzer

5. TraceViewer.java:
package advantage.utils;

import java.awt.Color;

import javax.swing.SwingUtilities;
import javax.swing.JTextPane;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

/**
 * <p>Title: TraceViewer</p>
 * <p>Description: The viewing component of the {@link TraceAnalyzer}.</p>
 * <p>Copyright: Copyright (c) 2004 by du-it</p>
 * <p>Company: du-it Dirk Ulrich-Informationstechnologie</p>
 * @author Dirk Ulrich d.ulrich@du-it.de
 * @version 1.0
 */

public class TraceViewer extends JTextPane
{
  private transient DefaultStyledDocument doc = null;
  private transient StyleContext sCtxt = null;
  private TraceGroup traceGroup = null;
  //----------------------------------------------------------------------------

  public TraceViewer(TraceGroup tGroup)
  {
    traceGroup = tGroup;
    sCtxt = new StyleContext();
    doc = new DefaultStyledDocument(sCtxt);
    this.setDocument(doc);

    final StringBuffer sb = traceGroup.getTraces();
this.setBackground(Color.green);
final MutableAttributeSet par_1 = new SimpleAttributeSet();
StyleConstants.setForeground(par_1, Color.black);
StyleConstants.setBackground(par_1, Color.gray);

/*
    final Style pStyle_1 = sContext.addStyle("paragraph_1_bg", null);
    final Style pStyle_2 = sContext.addStyle("paragraph_2_bg", null);
    final Style mainStyle = sContext.addStyle("mainStyle", sContext.getStyle(StyleContext.DEFAULT_STYLE));

    StyleConstants.setLeftIndent(mainStyle, 0);
    StyleConstants.setRightIndent(mainStyle, 80);

    pStyle_1.addAttribute(StyleConstants.FontSize, new Integer(20));
    pStyle_1.addAttribute(StyleConstants.Foreground, Color.blue);
    pStyle_1.addAttribute(StyleConstants.Background, Color.red);
*/
doc.setParagraphAttributes(0, 1, par_1, true);
this.replaceSelection(sb.toString());
    this.setEditable(false);
  }//end default constructor
  //----------------------------------------------------------------------------

  /**
   * Feeds the <code>TraceViewer</code> with the {@link TraceGroup} to be shown.
   * @param group The {@link TraceGroup} the be displayed through this
   * <code>TraceViewer</code>.
   * @author Dirk Ulrich
   */
  public void setTraceGroup(TraceGroup group)
  {
    this.traceGroup = group;
  }//end setTraceGroup
  //----------------------------------------------------------------------------

  public TraceGroup getTraceGroup()
  {
    return this.traceGroup;
  }//end getTraceGroup
  //----------------------------------------------------------------------------
}//end class TraceViewer

You need three propery files, too, as ResourceBundles:
1. traceAnalyzerConf.properties:
width = 800
height = 600

locale_language = en
locale_country = GB
#locale_language = en
#locale_country = US
#locale_language = de
#locale_country = DE

TraceAnalyzerBgColor = #c0c0c0
TraceAnalyzerBgColor_R = 225
TraceAnalyzerBgColor_G = 225
TraceAnalyzerBgColor_B = 225

TraceViewer1Color = #ffffff
TraceViewer2Color = #e1e1e1

2. traceAnalyzer_en_US.properties:
width = 800
height = 600

filenameLabel = Filename
openFileButtonLabel = open file
fileloadingProgressLabel = Loading file...
filegroupingProgressLabel = Grouping...

#Items within JComboBox for grouping criteria
criteria_TIMESTAMP       = Timestamp
criteria_PROCESS_ID      = Process ID
criteria_THREAD_ID       = Thread ID
criteria_TRACE_LEVEL     = Trace level
criteria_TRACE_TOPIC     = Trace topic
criteria_CFRAME_TRACETAG = CFRAME TRACETAG
criteria_CAT_TYPE_NAME   = CAT typename
criteria_CAT_INSTANCE_ID = CAT instance ID
criteria_CAT_TYPE_ID     = CAT type ID
criteria_COMPONENT_ID    = Component ID
criteria_CONTEXT_ID      = Context ID
criteria_CONTEXT_TYPE_ID = Context type ID
criteria_TRACE_MESSAGE   = Message

3. traceAnalyzer_de_DE.properties:
width = 800
height = 600

filenameLabel = Dateiname
openFileButtonLabel = Datei öffnen
fileloadingProgressLabel = Lade Datei...
filegroupingProgressLabel = Gruppiere...

#Items within JComboBox for grouping criteria
criteria_TIMESTAMP       = Zeitstempel
criteria_PROCESS_ID      = Prozess ID
criteria_THREAD_ID       = Thread ID
criteria_TRACE_TOPIC     = Trace Topic
criteria_TRACE_LEVEL     = Trace level
criteria_CFRAME_TRACETAG = CFRAME TRACETAG
criteria_CAT_TYPE_NAME   = CAT Typname
criteria_CAT_INSTANCE_ID = CAT Instanz ID
criteria_CAT_TYPE_ID     = CAT Typ ID
criteria_COMPONENT_ID    = Komponenten ID
criteria_CONTEXT_ID      = Kontext ID
criteria_CONTEXT_TYPE_ID = Kontext Typ ID
criteria_TRACE_MESSAGE   = Nachricht
ASKER CERTIFIED SOLUTION
Avatar of Webstorm
Webstorm

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
For the alignment problem :
Replace
      traceGroupJumperList.setAlignmentY(LEFT_ALIGNMENT);
by
      traceGroupJumperList.setAlignmentX(LEFT_ALIGNMENT);
Avatar of dirku

ASKER

Mmh. My app still hangs up.

Alignment: I played around with the alignment and I have already replaced it with .setAlignmentX(LEFT_ALIGNMENT); but the result is the same.

It's all so weird!  ;-)
Alignment :

Replace: BoxLayout.PAGE_AXIS
By:         BoxLayout.Y_AXIS
Avatar of dirku

ASKER

Although I couldn't make this code work in my particular app I think it's a good way to synchronize the threads and that there is something else why I still get the IllegalMonitorStateException.

Please, answer me a last question related to this issue:
Is this wait()/notify synchronization used only for Threads which are invoked within an application or can it be used for the main application which spawns some threads, either?
What I mean is:
I have a main app which spawns thread A and thread B. Can i used wait()/notify() only for the treads A and B or can I use it for the main app, too? More precisely, can I use wait()/notify() to stop the main app's execution until thread A and/or thread B finished their work? Should I preferably use A.join() or B.join() in the main app then? doesn't cause this an IllegalMonitorStateException or the starvation of the main app?