I'm working on an application that will monitor the status of servers
in my LDAP environments displaying the servers in a tree view grouped
by tree name. During a thread's connection check to see if the server
is responding, if the result is an error in connection, the thread
tells the JTree to expand the path to the tree that the server resides
in. For example:
Profile Navigator
My LDAP Tree (Expanded due to Server2 failure)
Server1
Server2 (Connection Failed)
etc...
My Other LDAP Tree
Another LDAP Tree
etc...
Here is a sample of the code that performs the expansion from within
the run() function of the thread (each thread checks only one server):
... connectivity test, set a warning variable on the
tree if a failure occurs
if (tree.isWarning()) {
if (profilePanel.isCollapsedPath(path)) {
profilePanel.expandPath(path);
}
}
else {
if (profilePanel.isExpandedPath(path)) {
profilePanel.collapsePath(path);
}
}
In my ProfilePanel (extends JTree) I override the
DefaultTreeCellRenderer to replace the icons of each entry so a server
has a server icon, tree has a tree icon, etc. In addition, a separate
icon is displayed when an error has occurred. In other words, the tree
icon now has an exclamation on it, as does the server icon of the
server that failed. Below is the code for my renderer:
public class ProfilePanel extends JTree implements ActionListener {
DefaultMutableTreeNode top;
Config config;
EventViewer evtViewer;
public ProfilePanel(String topnodeTitle) {
super(new DefaultMutableTreeNode(topnodeTitle));
top = (DefaultMutableTreeNode)getModel().getRoot();
setRowHeight(18);
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
setCellRenderer(new MyRenderer());
}
private class MyRenderer extends DefaultTreeCellRenderer {
ImageIcon rootIcon = createImageIcon("images/treegroup.gif");
ImageIcon serverIcon = createImageIcon("images/server.gif");
ImageIcon pserverIcon = createImageIcon("images/server_primary.gif");
ImageIcon treeIcon = createImageIcon("images/tree.gif");
ImageIcon dxmlIcon = createImageIcon("images/dirxml.gif");
ImageIcon dxmlWarningIcon =
createImageIcon("images/warning_dirxml.gif");
ImageIcon dxmlSetIcon =
createImageIcon("images/dirxml_driverset.gif");
ImageIcon dxmlSetWarnIcon =
createImageIcon("images/warning_dirxml_driverset.gif");
ImageIcon treewarningIcon =
createImageIcon("images/warning_tree.gif");
ImageIcon servwarningIcon =
createImageIcon("images/warning_server.gif");
ImageIcon pservwarningIcon =
createImageIcon("images/warning_server_primary.gif");
ImageIcon unknownIcon = createImageIcon("images/unknown.gif");
Font rootFont = new Font("Arial",Font.BOLD,11);
Font plainFont = new Font("Arial",Font.PLAIN,11);
Font primaryFont = new Font("Arial",Font.BOLD,11);
public MyRenderer() {
}
public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree,value,sel,expanded,leaf,row,hasFocus);
TreePath tp = (TreePath)getPathForRow(row);
setFont(plainFont);
if (isRoot(value)) {
setFont(rootFont);
setIcon(rootIcon);
}
else if ( (isTree(value)) && (isWarning(value)) ) {
setIcon(treewarningIcon); }
else if ( (isTree(value)) && (! isWarning(value)) ) {
setIcon(treeIcon); }
else if ( (isDirXML(value)) && (isWarning(value)) ) {
setIcon(dxmlWarningIcon); }
else if ( (isDirXML(value)) && (!isWarning(value)) ) {
setIcon(dxmlIcon); }
else if ( (isDirXML_Driverset(value)) && (isWarning(value)) ) {
setIcon(dxmlSetWarnIcon); }
else if ( (isDirXML_Driverset(value)) && (! isWarning(value)) ) {
setIcon(dxmlSetIcon); }
else if (isServer(value)) {
if (isPrimary(value)) {
setFont(primaryFont);
if (isWarning(value)) { setIcon(pservwarningIcon); }
else { setIcon(pserverIcon); }
}
else {
if (isWarning(value)) { setIcon(servwarningIcon); }
else { setIcon(serverIcon); }
}
}
else if (leaf) { setIcon(unknownIcon); }
else { setIcon(unknownIcon); }
return this;
}
}
protected boolean isRoot(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.equals((DefaultMutableTreeNode)getModel().getRoot())) {
return true;
}
else { return false; }
}
protected boolean isDirXML(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
String nodename = node.getUserObject().toString();
return config.drivers.containsKey(nodename);
}
protected boolean isDirXML_Driverset(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
String nodename = node.getUserObject().toString();
return config.driversets.containsKey(nodename);
}
protected boolean isTree(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
String nodename = node.getUserObject().toString();
return config.trees.containsKey(nodename);
}
protected boolean isWarning(Object value) {
boolean result = false;
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
String nodename = node.getUserObject().toString();
try {
LDAPTree tree = (LDAPTree)config.trees.get(nodename);
result = tree.isWarning();
}
catch (Exception e) {
// Not a tree
}
try {
LDAPServer serv = (LDAPServer)config.servers.get(nodename);
result = serv.isWarning();
}
catch (Exception e) {
// Not a server
}
try {
XMLDriver driver = (XMLDriver)config.drivers.get(nodename);
result = driver.isWarning();
}
catch (Exception e) {
// Not a driver
}
try {
XMLDriverSet set = (XMLDriverSet)config.driversets.get(nodename);
result = set.isWarning();
}
catch (Exception e) {
// Not a driver
}
return result;
}
protected boolean isServer(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
String nodename = node.getUserObject().toString();
return config.servers.containsKey(nodename);
}
protected boolean isPrimary(Object value) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
for (Enumeration e = config.trees.elements(); e.hasMoreElements(); )
{
LDAPTree tree = (LDAPTree)e.nextElement();
if
(node.getUserObject().toString().equalsIgnoreCase(tree.getPrimary())) {
return true; }
}
return false;
}
}
If I run my program WITHOUT this custom renderer, I give up all the
custom icons, but the expansion and collapse of tree nodes via my
thread works perfectly. When I turn ON the custom renderer, I
sometimes (which is very confusing) see problems where servers will
jump between LDAP trees in the JTree and/or will leave blank gaps in
the tree where the server used to be. I have also noticed some of the
servers get displayed directly on TOP of the root node, which screws up
the whole JTree and I have to restart my application to clear it up.
Expanding and/or collapsing the tree that the server jumps into does
not recognize that the servers were placed under it. When I collapse
and expand the root node, assuming it didn't disappear or get corrupted
somehow, all is well again until I perform the next server check. I
even tried taking out ALL the logic of the DefaultTreeCellRenderer
except for the constructor, and I still see these issues when the nodes
are expanded and collapsed with the default icons.
Any ideas on why this is happening? Please let me know what other code
you might like to see in order to assist. Please be easy on me, this
is my first Java application and things are working out very nicely
except for this minor annoyance. Thanks!
Kari Ikonen - 24 Nov 2005 20:58 GMT
> is responding, if the result is an error in connection, the thread
> tells the JTree to expand the path to the tree that the server resides
<snip>
> jump between LDAP trees in the JTree and/or will leave blank gaps in
> the tree where the server used to be. I have also noticed some of the
> servers get displayed directly on TOP of the root node, which screws up
> the whole JTree and I have to restart my application to clear it up.
Seen such behaviour and it has nothing to do with renderer. Swing EDT safety
is just broken. Swing components can be accessed only from EDT -thread.
@see SwingUtilities.invokeLater()

Signature
KI -- http://kari.world.dy.fi
webguyatwork - 30 Nov 2005 20:37 GMT
The SwingUtilities.invokeLater() call seems to be a great fix for my
JTree update issue, but now ALL the threads wait to finish before
updating anything. I want my JTable to update with the textual status
and a [Green/Red] icon for status as each thread completes, and expand
or collapse my JTree node at the same time rather than wait for all the
threads to complete. How can I accomplish this?