package ecologylab.concurrent;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Observer;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import ecologylab.generic.Debug;
import ecologylab.generic.ObservableDebug;
/**
* @author vikrams
*
* To change this generated comment edit the template variable "typecomment":
* Window>Preferences>Java>Templates.
* To enable and disable the creation of type comments go to
* Window>Preferences>Java>Code Generation.
*/
public class ThreadDebugger extends Debug
{
static Hashtable threadEntriesByName = new Hashtable();
static HashMap threadEntriesByThread = new HashMap();
static int nThreads;
static JFrame threadControlFrame = new JFrame("Thread Debugger");
static JPanel threadControlPanel = new JPanel();
static Box verticalBox = new Box(BoxLayout.Y_AXIS);
static ActionListener threadToggler;
static WindowObservable windowObservable = new WindowObservable();
static
{
nThreads = 0;
threadToggler = new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
String action = e.getActionCommand();
// ignore "start " / "pause " - we have the thread name as the key into the hashtable
String threadName = action.substring(6,action.length());
ThreadEntry ttd = (ThreadEntry)threadEntriesByName.get(threadName);
if (ttd == null)
{
// System.err.println("\nthreadName = " + threadName);
return;
}
boolean paused = toggleAndReturnNewState(ttd);
if (!paused)
{
resume(ttd);
}
}
};
threadControlPanel.add(verticalBox);
threadControlFrame.getContentPane().add(threadControlPanel);
threadControlFrame.pack();
setPosition();
threadControlFrame.addWindowListener(windowObservable);
}
public ThreadDebugger()
{
System.err.println("\nThreadDebugger constructor");
}
public static void registerMyself(Thread thread)
{
if (threadControlFrame == null)
return;
synchronized (threadEntriesByName)
{
final String threadName = thread.getName();
if (threadEntriesByName.get(threadName) != null)
{
return;
}
ThreadEntry threadEntry = new ThreadEntry(thread);
threadEntriesByName.put(threadName, threadEntry);
threadEntriesByThread.put(thread, threadEntry);
nThreads++;
//println("ThreadDebugger.register("+thread+" COUNT = " +nThreads);
verticalBox.add(threadEntry.button);
threadControlFrame.pack();
setPosition();
}
}
public static boolean toggleAndReturnNewState(ThreadEntry threadEntry)
{
threadEntry.button.setBackground(Color.yellow);
return threadEntry.toggleAndReturnNewState();
}
public static void waitIfPaused(Thread thread)
{
/* do nothing, because this may be the cause of race conditions, and no one has used it recently -- andruid 2/28/07
ThreadEntry threadEntry = (ThreadEntry)threadEntriesByThread.get(thread);
if( threadEntry != null)
{
Object mylock = threadEntry.lock;
synchronized (mylock)
{
boolean paused = threadEntry.paused;
if (paused)
{
try
{
println("\nPAUSING THREAD " + thread.getName());
threadEntry.button.setBackground(Color.red);
mylock.wait();
}catch (InterruptedException e)
{
}
}
}
}
*/
}
public static void resume(ThreadEntry threadEntry)
{
Object mylock = threadEntry.lock;
synchronized (mylock)
{
println("\nRESTARTING THREAD " + threadEntry.thread.getName());
mylock.notify();
threadEntry.button.setBackground(Color.green);
}
}
public static void removeMyself(Thread thread)
{
ThreadEntry removedTtd = (ThreadEntry)threadEntriesByThread.remove(thread.getName());
threadEntriesByName.remove(thread);
verticalBox.remove(removedTtd.button);
threadControlFrame.pack();
setPosition();
}
/**
* Clear all the thread collections -- eunyee
*
*/
public static void clear()
{
threadEntriesByName.clear();
threadEntriesByThread.clear();
nThreads = 0;
}
static int xOriginal, yOriginal;
public static void setPosition(int x, int y)
{
xOriginal = x;
yOriginal = y;
threadControlFrame.setLocation(x - currentWidth(),y - currentHeight());
}
static void setPosition()
{
threadControlFrame.setLocation(xOriginal - currentWidth(),yOriginal - currentHeight());
}
protected static int currentWidth()
{
return threadControlFrame.getWidth();
}
protected static int currentHeight()
{
return threadControlFrame.getHeight();
}
public static void show()
{
println("ThreadDebugger.show()");
threadControlFrame.setVisible(true);
}
public static void hide()
{
println("ThreadDebugger.hide()");
threadControlFrame.setVisible(false);
}
public static void addObserver(Observer o)
{
windowObservable.addObserver(o);
}
static class WindowObservable extends ObservableDebug
implements WindowListener
{
@Override
public void windowClosing(WindowEvent e)
{
println("ThreadDebugger.windowClosing()");
setChanged();
notifyObservers("thread_debugger_close");
}
@Override
public void windowOpened(WindowEvent e)
{
}
@Override
public void windowClosed(WindowEvent e)
{
}
@Override
public void windowIconified(WindowEvent e)
{
}
@Override
public void windowDeiconified(WindowEvent e)
{
}
@Override
public void windowActivated(WindowEvent e)
{
}
@Override
public void windowDeactivated(WindowEvent e)
{
}
}
}
/**
* An entry in the Hash of debuggable threads.
*
* @author andruid
*/
class ThreadEntry
{
Object lock = new Object();
JButton button;
Thread thread;
boolean paused = false;
ThreadEntry(Thread thread)
{
this.thread = thread;
button = new JButton("Pause " + thread.getName());
button.addActionListener(ThreadDebugger.threadToggler);
}
public boolean toggleAndReturnNewState()
{
paused = !paused;
if (!paused)
{
button.setText("Pause " + thread.getName());
}
else
{
button.setText("Start " + thread.getName());
}
return paused;
}
}