View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.chainsaw.receivers;
19  
20  import java.awt.*;
21  import java.awt.event.ActionEvent;
22  import java.awt.event.ActionListener;
23  import java.awt.event.KeyEvent;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import java.io.File;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.Action;
34  import javax.swing.BorderFactory;
35  import javax.swing.Box;
36  import javax.swing.Icon;
37  import javax.swing.ImageIcon;
38  import javax.swing.JComponent;
39  import javax.swing.JDialog;
40  import javax.swing.JFrame;
41  import javax.swing.JMenuItem;
42  import javax.swing.JOptionPane;
43  import javax.swing.JPanel;
44  import javax.swing.JPopupMenu;
45  import javax.swing.JRadioButtonMenuItem;
46  import javax.swing.JScrollPane;
47  import javax.swing.JSplitPane;
48  import javax.swing.JToolBar;
49  import javax.swing.JTree;
50  import javax.swing.SwingUtilities;
51  import javax.swing.event.TreeExpansionEvent;
52  import javax.swing.event.TreeModelEvent;
53  import javax.swing.event.TreeModelListener;
54  import javax.swing.event.TreeSelectionEvent;
55  import javax.swing.event.TreeSelectionListener;
56  import javax.swing.event.TreeWillExpandListener;
57  import javax.swing.tree.DefaultMutableTreeNode;
58  import javax.swing.tree.ExpandVetoException;
59  import javax.swing.tree.TreePath;
60  
61  import org.apache.log4j.Level;
62  import org.apache.log4j.LogManager;
63  import org.apache.log4j.Logger;
64  import org.apache.log4j.chainsaw.PopupListener;
65  import org.apache.log4j.chainsaw.SmallButton;
66  import org.apache.log4j.chainsaw.prefs.SettingsManager;
67  import org.apache.log4j.chainsaw.prefs.SettingsListener;
68  import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent;
69  import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent;
70  import org.apache.log4j.chainsaw.help.HelpManager;
71  import org.apache.log4j.chainsaw.helper.SwingHelper;
72  import org.apache.log4j.chainsaw.icons.ChainsawIcons;
73  import org.apache.log4j.chainsaw.icons.LevelIconFactory;
74  import org.apache.log4j.chainsaw.icons.LineIconFactory;
75  import org.apache.log4j.chainsaw.messages.MessageCenter;
76  import org.apache.log4j.net.SocketNodeEventListener;
77  import org.apache.log4j.net.SocketReceiver;
78  import org.apache.log4j.plugins.Pauseable;
79  import org.apache.log4j.plugins.Plugin;
80  import org.apache.log4j.plugins.PluginEvent;
81  import org.apache.log4j.plugins.PluginListener;
82  import org.apache.log4j.plugins.PluginRegistry;
83  import org.apache.log4j.plugins.Receiver;
84  import org.apache.log4j.spi.LoggerRepository;
85  import org.apache.log4j.spi.LoggerRepositoryEx;
86  
87  
88  /**
89   * This panel is used to manage all the Receivers configured within Log4j
90   *
91   *
92   * @author Paul Smith <psmith@apache.org>
93   * @author Scott Deboy <sdeboy@apache.org>
94   */
95  public class ReceiversPanel extends JPanel implements SettingsListener {
96    final Action newReceiverButtonAction;
97    final Action pauseReceiverButtonAction;
98    final Action playReceiverButtonAction;
99    final Action shutdownReceiverButtonAction;
100   final Action saveReceiversButtonAction;
101   final Action restartReceiverButtonAction;
102   private final Action showReceiverHelpAction;
103   private final Action startAllAction;
104   private final JPopupMenu popupMenu = new ReceiverPopupMenu();
105   private final JTree receiversTree = new JTree();
106   private final NewReceiverPopupMenu newReceiverPopup =
107     new NewReceiverPopupMenu();
108   private final ReceiverToolbar buttonPanel;
109   private final JSplitPane splitter = new JSplitPane();
110   private final PluginPropertyEditorPanel pluginEditorPanel =
111     new PluginPropertyEditorPanel();
112   private final Logger logger = LogManager.getLogger(ReceiversPanel.class);
113   
114   private final PluginRegistry pluginRegistry;
115   
116 
117   public ReceiversPanel() {
118     super(new BorderLayout());
119     LoggerRepository repo = LogManager.getLoggerRepository();
120     final ReceiversTreeModel model = new ReceiversTreeModel();
121     if (repo instanceof LoggerRepositoryEx) {
122        pluginRegistry = ((LoggerRepositoryEx) repo).getPluginRegistry();
123        pluginRegistry.addPluginListener(model);
124 
125        //iterate over visual receivers and call setcontainer
126        Collection c = pluginRegistry.getPlugins(VisualReceiver.class);
127        for (Iterator iter = c.iterator();iter.hasNext();) {
128     	   ((VisualReceiver)iter.next()).setContainer(this);
129        }
130        
131        pluginRegistry.addPluginListener(new PluginListener() {
132 		public void pluginStarted(PluginEvent e) {
133 			//if we get a plugin started callback, set the container
134 			if (e.getPlugin() instanceof VisualReceiver) {
135 				((VisualReceiver)e.getPlugin()).setContainer(ReceiversPanel.this);
136 			}
137 		}
138 
139 		public void pluginStopped(PluginEvent e) {
140 		}
141        });
142     } else {
143        pluginRegistry = null;
144     }
145     
146     receiversTree.setModel(model);
147 
148     receiversTree.setExpandsSelectedPaths(true);
149     model.addTreeModelListener(
150       new TreeModelListener() {
151         public void treeNodesChanged(TreeModelEvent e) {
152           expandRoot();
153         }
154 
155         public void treeNodesInserted(TreeModelEvent e) {
156           expandRoot();
157         }
158 
159         public void treeNodesRemoved(TreeModelEvent e) {
160           expandRoot();
161         }
162 
163         public void treeStructureChanged(TreeModelEvent e) {
164           expandRoot();
165         }
166 
167         private void expandRoot() {
168           receiversTree.expandPath(
169             new TreePath(model.getPathToRoot(model.RootNode)));
170         }
171       });
172     receiversTree.expandPath(
173       new TreePath(model.getPathToRoot(model.RootNode)));
174 
175     receiversTree.addTreeWillExpandListener(
176       new TreeWillExpandListener() {
177         public void treeWillCollapse(TreeExpansionEvent event)
178           throws ExpandVetoException {
179           if (event.getPath().getLastPathComponent() == model.RootNode) {
180             throw new ExpandVetoException(event);
181           }
182         }
183 
184         public void treeWillExpand(TreeExpansionEvent event)
185           throws ExpandVetoException {
186         }
187       });
188 
189     receiversTree.addTreeSelectionListener(
190       new TreeSelectionListener() {
191         public void valueChanged(TreeSelectionEvent e) {
192           TreePath path = e.getNewLeadSelectionPath();
193 
194           if (path != null) {
195             DefaultMutableTreeNode node =
196               (DefaultMutableTreeNode) path.getLastPathComponent();
197 
198             if (
199               (node != null) && (node.getUserObject() != null)
200                 && (node.getUserObject() instanceof Plugin)) {
201               Plugin p = (Plugin) node.getUserObject();
202               logger.debug("plugin=" + p);
203               pluginEditorPanel.setPlugin(p);
204             } else {
205               pluginEditorPanel.setPlugin(null);
206             }
207           }
208         }
209       });
210 
211     receiversTree.setToolTipText("Allows you to manage Log4j Receivers");
212     newReceiverButtonAction =
213       new AbstractAction() {
214           public void actionPerformed(ActionEvent e) {
215             newReceiverPopup.show(
216               buttonPanel.newReceiverButton, 0,
217               buttonPanel.newReceiverButton.getHeight());
218           }
219         };
220     newReceiverButtonAction.putValue(
221       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.ICON_NEW_RECEIVER));
222     newReceiverButtonAction.putValue(
223       Action.SHORT_DESCRIPTION, "Creates and configures a new Receiver");
224     newReceiverButtonAction.putValue(Action.NAME, "New Receiver");
225     newReceiverButtonAction.putValue(
226       Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_N));
227 
228     newReceiverButtonAction.setEnabled(true);
229 
230     playReceiverButtonAction =
231       new AbstractAction() {
232           public void actionPerformed(ActionEvent e) {
233             playCurrentlySelectedReceiver();
234           }
235         };
236 
237     playReceiverButtonAction.putValue(
238       Action.SHORT_DESCRIPTION, "Resumes the selected Node");
239     playReceiverButtonAction.putValue(Action.NAME, "Resume");
240     playReceiverButtonAction.putValue(
241       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.ICON_RESUME_RECEIVER));
242     playReceiverButtonAction.setEnabled(false);
243     playReceiverButtonAction.putValue(
244       Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
245 
246     pauseReceiverButtonAction =
247       new AbstractAction() {
248           public void actionPerformed(ActionEvent e) {
249             pauseCurrentlySelectedReceiver();
250           }
251         };
252 
253     pauseReceiverButtonAction.putValue(
254       Action.SHORT_DESCRIPTION,
255       "Pause the selected Receiver.  All events received will be discarded.");
256     pauseReceiverButtonAction.putValue(Action.NAME, "Pause");
257 
258     pauseReceiverButtonAction.putValue(
259       Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
260 
261     pauseReceiverButtonAction.putValue(
262       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.PAUSE));
263     pauseReceiverButtonAction.setEnabled(false);
264 
265     shutdownReceiverButtonAction =
266       new AbstractAction() {
267           public void actionPerformed(ActionEvent e) {
268             shutdownCurrentlySelectedReceiver();
269           }
270         };
271 
272     shutdownReceiverButtonAction.putValue(
273       Action.SHORT_DESCRIPTION,
274       "Shuts down the selected Receiver, and removes it from the Plugin registry");
275     shutdownReceiverButtonAction.putValue(Action.NAME, "Shutdown");
276 
277     shutdownReceiverButtonAction.putValue(
278       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.ICON_STOP_RECEIVER));
279     shutdownReceiverButtonAction.putValue(
280       Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
281 
282     shutdownReceiverButtonAction.setEnabled(false);
283 
284     saveReceiversButtonAction =
285       new AbstractAction() {
286           public void actionPerformed(ActionEvent e) {
287             saveReceivers();
288           }
289         };
290 
291     saveReceiversButtonAction.putValue(
292       Action.SHORT_DESCRIPTION,
293       "Save the current receiver configuration");
294     saveReceiversButtonAction.putValue(Action.NAME, "Save receivers");
295 
296     saveReceiversButtonAction.putValue(
297       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.FILE_SAVE_AS));
298     saveReceiversButtonAction.putValue(
299       Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_V));
300 
301 
302     restartReceiverButtonAction =
303         new AbstractAction() {
304             public void actionPerformed(ActionEvent e) {
305               Receiver selectedReceiver = getCurrentlySelectedReceiver();
306               if(selectedReceiver == null){
307               	return;
308               }
309               selectedReceiver.shutdown();
310               selectedReceiver.activateOptions();
311               //allow the visual receiver to get a container on restart
312               if (selectedReceiver instanceof VisualReceiver) {
313                   ((VisualReceiver)selectedReceiver).setContainer(ReceiversPanel.this);
314               }
315             }
316           };
317 
318       restartReceiverButtonAction.putValue(
319         Action.SHORT_DESCRIPTION,
320         "Restarts the selected Receiver");
321       restartReceiverButtonAction.putValue(Action.NAME, "Restart");
322 
323       restartReceiverButtonAction.putValue(
324         Action.SMALL_ICON, new ImageIcon(ChainsawIcons.ICON_RESTART));
325       restartReceiverButtonAction.putValue(
326         Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
327 
328       restartReceiverButtonAction.setEnabled(false);
329 
330     showReceiverHelpAction =
331       new AbstractAction("Help") {
332           public void actionPerformed(ActionEvent e) {
333             Receiver receiver = getCurrentlySelectedReceiver();
334 
335             if (receiver != null) {
336               HelpManager.getInstance().showHelpForClass(receiver.getClass());
337             }
338           }
339         };
340 
341     showReceiverHelpAction.putValue(
342       Action.SMALL_ICON, new ImageIcon(ChainsawIcons.HELP));
343     showReceiverHelpAction.putValue(
344       Action.SHORT_DESCRIPTION, "Displays the JavaDoc page for this Plugin");
345 
346     startAllAction =
347       new AbstractAction(
348         "(Re)start All Receivers", new ImageIcon(ChainsawIcons.ICON_RESTART_ALL)) {
349           public void actionPerformed(ActionEvent e) {
350             if (
351               JOptionPane.showConfirmDialog(
352                   null,
353                   "This will cause any active Receiver to stop, and disconnect.  Is this ok?",
354                   "Confirm", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
355               new Thread(
356                 new Runnable() {
357                   public void run() {
358                     Collection allReceivers =
359                         pluginRegistry.getPlugins(Receiver.class);
360 
361                     for (Iterator iter = allReceivers.iterator();
362                         iter.hasNext();) {
363                       Receiver item = (Receiver) iter.next();
364                       item.shutdown();
365                       item.activateOptions();
366                     }
367 
368                     updateReceiverTreeInDispatchThread();
369                     MessageCenter.getInstance().getLogger().info(
370                       "All Receivers have been (re)started");
371                   }
372                 }).start();
373             }
374           }
375         };
376 
377     startAllAction.putValue(
378       Action.SHORT_DESCRIPTION,
379       "Ensures that any Receiver that isn't active, is started, and any started action is stopped, and then restarted");
380 
381     receiversTree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
382     receiversTree.setCellRenderer(new ReceiverTreeCellRenderer());
383     receiversTree.setRowHeight(19);
384 
385     buttonPanel = new ReceiverToolbar();
386     receiversTree.addTreeSelectionListener(buttonPanel);
387 
388     PopupListener popupListener = new PopupListener(popupMenu);
389     receiversTree.addMouseListener(popupListener);
390     this.addMouseListener(popupListener);
391 
392     JComponent component = receiversTree;
393     JScrollPane pane = new JScrollPane(component);
394 
395     splitter.setOrientation(JSplitPane.VERTICAL_SPLIT);
396 
397     splitter.setTopComponent(pane);
398     splitter.setBottomComponent(pluginEditorPanel);
399 
400     splitter.setResizeWeight(0.7);
401     add(buttonPanel, BorderLayout.NORTH);
402     add(splitter, BorderLayout.CENTER);
403 
404     /**
405      * This Tree likes to be notified when Socket's are accepted so
406      * we listen for them and update the Tree.
407      */
408     SocketNodeEventListener listener =
409       new SocketNodeEventListener() {
410         public void socketOpened(String remoteInfo) {
411           updateReceiverTreeInDispatchThread();
412         }
413 
414         public void socketClosedEvent(Exception e) {
415           updateReceiverTreeInDispatchThread();
416         }
417       };
418 
419     /**
420      * add this listener to all SocketReceivers
421      */
422     if (pluginRegistry != null) {
423     	List socketReceivers =
424       		pluginRegistry.getPlugins(SocketReceiver.class);
425 
426     	for (Iterator iter = socketReceivers.iterator(); iter.hasNext();) {
427       		SocketReceiver element = (SocketReceiver) iter.next();
428       		element.addSocketNodeEventListener(listener);
429     	}
430      }
431   }
432 
433   private void saveReceivers() {
434     File saveConfigFile = SwingHelper.promptForFile(this, null, "Save receiver configuration XML file", false);
435     if (saveConfigFile != null) {
436       ReceiversHelper.getInstance().saveReceiverConfiguration(saveConfigFile);
437     }
438   }
439 
440   protected ReceiversTreeModel getReceiverTreeModel() {
441     return ((ReceiversTreeModel) receiversTree.getModel());
442   }
443 
444   /**
445    *
446    */
447   protected void updateCurrentlySelectedNodeInDispatchThread() {
448     SwingUtilities.invokeLater(
449       new Runnable() {
450         public void run() {
451           DefaultMutableTreeNode node =
452             (DefaultMutableTreeNode) receiversTree
453             .getLastSelectedPathComponent();
454 
455           if (node == null) {
456             return;
457           }
458 
459           getReceiverTreeModel().nodeChanged(node);
460           updateActions();
461         }
462       });
463   }
464 
465   /**
466    * Returns the currently selected Receiver, or null if there is no
467    * selected Receiver (this could be because a) nothing at all is selected
468    * or b) a non Receiver type node is selected
469    *
470    * @return Receiver or null
471    */
472   private Receiver getCurrentlySelectedReceiver() {
473     DefaultMutableTreeNode node =
474       (DefaultMutableTreeNode) receiversTree.getLastSelectedPathComponent();
475 
476     if (node == null) {
477       return null;
478     }
479 
480     Object userObject = node.getUserObject();
481 
482     if (userObject instanceof Receiver) {
483       return (Receiver) userObject;
484     }
485 
486     return null;
487   }
488 
489   private Receiver[] getSelectedReceivers() {
490     TreePath[] paths = receiversTree.getSelectionPaths();
491     Collection receivers = new ArrayList();
492 
493     for (int i = 0; i < paths.length; i++) {
494       TreePath path = paths[i];
495       DefaultMutableTreeNode node =
496         (DefaultMutableTreeNode) path.getLastPathComponent();
497 
498       if ((node != null) && node.getUserObject() instanceof Receiver) {
499         receivers.add(node.getUserObject());
500       }
501     }
502 
503     return (Receiver[]) receivers.toArray(new Receiver[0]);
504   }
505 
506   /**
507    * Returns the currently seleted node's User Object, or null
508    * if there is no selected node, or if the currently selected node has
509    * not user Object
510    * @return Object representing currently seleted Node's User Object
511    */
512   private Object getCurrentlySelectedUserObject() {
513     DefaultMutableTreeNode node =
514       (DefaultMutableTreeNode) receiversTree.getLastSelectedPathComponent();
515 
516     if (node == null) {
517       return null;
518     }
519 
520     return node.getUserObject();
521   }
522 
523   /**
524    * Takes the currently selected Receiver and pauess it, effectively
525    * discarding any received event BEFORE it is even posted to the logger
526    * repository.
527    *
528    * The user is NOT asked to confirm this operation
529    *
530    */
531   private void pauseCurrentlySelectedReceiver() {
532     new Thread(
533       new Runnable() {
534         public void run() {
535           Object obj = getCurrentlySelectedUserObject();
536 
537           if ((obj != null) && obj instanceof Pauseable) {
538             ((Pauseable) obj).setPaused(true);
539             updateCurrentlySelectedNodeInDispatchThread();
540           }
541         }
542       }).start();
543   }
544 
545   /**
546    * Ensures that the currently selected receiver active property is set to
547    * true
548    *
549    */
550   private void playCurrentlySelectedReceiver() {
551     new Thread(
552       new Runnable() {
553         public void run() {
554           Object obj = getCurrentlySelectedUserObject();
555 
556           if ((obj != null) && obj instanceof Pauseable) {
557             ((Pauseable) obj).setPaused(false);
558 
559             updateCurrentlySelectedNodeInDispatchThread();
560           }
561         }
562       }).start();
563   }
564 
565   /**
566    * Takes the currently selected Receiver and stops it, which effectively
567    * removes it from the PluginRegistry.
568    *
569    * The user is asked to confirm this operation
570    *
571    */
572   private void shutdownCurrentlySelectedReceiver() {
573     if (
574       JOptionPane.showConfirmDialog(
575           null,
576           "Are you sure you wish to shutdown this receiver?\n\nThis will disconnect any network resources, and remove it from the PluginRegistry.",
577           "Confirm stop of Receiver", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
578       new Thread(
579         new Runnable() {
580           public void run() {
581             Receiver[] receivers = getSelectedReceivers();
582 
583             if (receivers != null) {
584               for (int i = 0; i < receivers.length; i++) {
585                 pluginRegistry.stopPlugin(receivers[i].getName());
586               }
587             }
588           }
589         }).start();
590     }
591   }
592 
593   /**
594    * Sets the state of actions depending on certain conditions (i.e what is
595    * currently selected etc.)
596    */
597   private void updateActions() {
598     Object object = getCurrentlySelectedUserObject();
599 
600     if ((object != null) && object instanceof Pauseable) {
601       Pauseable pauseable = (Pauseable) object;
602 
603       if (!pauseable.isPaused()) {
604         pauseReceiverButtonAction.setEnabled(true);
605         playReceiverButtonAction.setEnabled(false);
606       } else {
607         pauseReceiverButtonAction.setEnabled(false);
608         playReceiverButtonAction.setEnabled(true);
609       }
610     } else {
611       pauseReceiverButtonAction.setEnabled(false);
612       playReceiverButtonAction.setEnabled(false);
613     }
614 
615     if (object instanceof Receiver) {
616       newReceiverButtonAction.setEnabled(true);
617       shutdownReceiverButtonAction.setEnabled(true);
618       restartReceiverButtonAction.setEnabled(true);
619     } else {
620       shutdownReceiverButtonAction.setEnabled(false);
621       restartReceiverButtonAction.setEnabled(false);
622     }
623   }
624 
625   /**
626    * Ensures that the Receiver tree is updated with the latest information
627    * and that this operation occurs in the Swing Event Dispatch thread.
628    *
629    */
630   public void updateReceiverTreeInDispatchThread() {
631     logger.debug(
632       "updateReceiverTreeInDispatchThread, should not be needed now");
633 
634     //    if (SwingUtilities.isEventDispatchThread()) {
635     //      updateReceiverTree.run();
636     //    } else {
637     //      SwingUtilities.invokeLater(updateReceiverTree);
638     //    }
639   }
640 
641   /* (non-Javadoc)
642    * @see java.awt.Component#setVisible(boolean)
643    */
644   public void setVisible(boolean aFlag) {
645     boolean oldValue = isVisible();
646     super.setVisible(aFlag);
647     firePropertyChange("visible", oldValue, isVisible());
648   }
649 
650   public void loadSettings(LoadSettingsEvent event){}
651 
652    /**
653     * Saves all the receivers which are active at shut down as a configuration
654     * file which can be loaded when Chainsaw will be restarted.
655     */
656 
657   public void saveSettings(SaveSettingsEvent event){
658      File file = new File(SettingsManager.getInstance().getSettingsDirectory(), "receiver-config.xml");
659      ReceiversHelper.getInstance().saveReceiverConfiguration(file);
660   }
661 
662   /**
663    * A popup menu that allows the user to choose which
664    * style of Receiver to create, which spawns a relevant Dialog
665    * to enter the information and create the Receiver
666    *
667    * @author Paul Smith &lt;psmith@apache.org&gt;
668    *
669    */
670   class NewReceiverPopupMenu extends JPopupMenu {
671     NewReceiverPopupMenu() {
672       try {
673         final List receiverList =
674           ReceiversHelper.getInstance().getKnownReceiverClasses();
675         String separatorCheck = null;
676 
677         for (Iterator iter = receiverList.iterator(); iter.hasNext();) {
678           final Class toCreate = (Class) iter.next();
679           Package thePackage = toCreate.getPackage();
680           final String name =
681             toCreate.getName().substring(thePackage.getName().length() + 1);
682 
683           if (separatorCheck == null) {
684             separatorCheck = name.substring(0, 1);
685           } else {
686             String current = name.substring(0, 1);
687 
688             if (!current.equals(separatorCheck)) {
689               addSeparator();
690               separatorCheck = current;
691             }
692           }
693 
694           add(
695             new AbstractAction("New " + name + "...") {
696               public void actionPerformed(ActionEvent e) {
697                 Container container = SwingUtilities.getAncestorOfClass(JFrame.class, ReceiversPanel.this);
698                 final JDialog dialog = new JDialog((JFrame) container,"New " + toCreate.getName() + "..." ,true);
699                 
700                 try {
701                   final NewReceiverDialogPanel panel =
702                     NewReceiverDialogPanel.create(toCreate);
703                   dialog.getContentPane().add(panel);
704                   dialog.pack();
705                   SwingHelper.centerOnScreen(dialog);
706 
707                   /**
708                    * Make the default button the ok button
709                    */
710                   dialog.getRootPane().setDefaultButton(panel.getOkPanel().getOkButton());
711                   
712                   /**
713                    * Use the standard Cancel metaphor
714                    */
715                   SwingHelper.configureCancelForDialog(dialog, panel.getOkPanel().getCancelButton());
716                   
717 
718                   panel.getOkPanel().getOkButton().addActionListener(
719                     new ActionListener() {
720                       public void actionPerformed(ActionEvent e2) {
721                         Plugin plugin = panel.getPlugin();
722                         if (plugin.getName() != null && !plugin.getName().trim().equals("")) {
723                             dialog.dispose();
724                             pluginRegistry.addPlugin(plugin);
725                             plugin.activateOptions();
726                             MessageCenter.getInstance().addMessage("Plugin '" + plugin.getName() + "' started");
727                         } else {
728                             MessageCenter.getInstance().getLogger().error("Name required to create receiver");
729                         }
730                       }
731                     });
732                   dialog.setVisible(true);
733                 } catch (Exception e1) {
734                   e1.printStackTrace();
735                   MessageCenter.getInstance().getLogger().error(
736                     "Failed to create the new Receiver dialog", e1);
737                 }
738               }
739             });
740         }
741       } catch (Exception e) {
742         e.printStackTrace();
743         throw new RuntimeException(e.getMessage());
744       }
745     }
746   }
747 
748   /**
749    * A popup menu class for when the user uses the popup trigger action
750    * on a node in the Receiver tree.
751    *
752    * @author Paul Smith &lt;psmith@apache.org&gt;
753    *
754    */
755   class ReceiverPopupMenu extends JPopupMenu {
756     ReceiverPopupMenu() {
757     }
758 
759     /* (non-Javadoc)
760      * @see javax.swing.JPopupMenu#show(java.awt.Component, int, int)
761      */
762     public void show(Component invoker, int x, int y) {
763       DefaultMutableTreeNode node =
764         (DefaultMutableTreeNode) receiversTree.getLastSelectedPathComponent();
765 
766       if (node == null) {
767         return;
768       }
769 
770       Object userObject = node.getUserObject();
771       removeAll();
772 
773       if (userObject == getRootOfTree().getUserObject()) {
774         buildForReceiversRoot();
775       } else if (getCurrentlySelectedReceiver() != null) {
776         buildForReceiverNode();
777       } else {
778         return;
779       }
780 
781       this.invalidate();
782       this.validate();
783 
784       super.show(invoker, x, y);
785     }
786 
787     /**
788      *
789      */
790     private DefaultMutableTreeNode getRootOfTree() {
791       return (DefaultMutableTreeNode) receiversTree.getModel().getRoot();
792     }
793 
794     /**
795      * Builds the popup menu with relevant items for a selected
796      * Receiver node in the Tree.
797      */
798     private void buildForReceiverNode() {
799 
800       add(playReceiverButtonAction);
801       add(pauseReceiverButtonAction);
802       add(restartReceiverButtonAction);
803       add(shutdownReceiverButtonAction);
804       add(saveReceiversButtonAction);
805 
806       addSeparator();
807 
808       final Receiver r = getCurrentlySelectedReceiver();
809       add(createLevelRadioButton(r, Level.TRACE));
810       add(createLevelRadioButton(r, Level.DEBUG));
811       add(createLevelRadioButton(r, Level.INFO));
812       add(createLevelRadioButton(r, Level.WARN));
813       add(createLevelRadioButton(r, Level.ERROR));
814       addSeparator();
815       add(createLevelRadioButton(r, Level.OFF));
816       add(createLevelRadioButton(r, Level.ALL));
817       addSeparator();
818       add(showReceiverHelpAction);
819     }
820 
821     private JRadioButtonMenuItem createLevelRadioButton(
822       final Receiver r, final Level l) {
823       Map levelIconMap = LevelIconFactory.getInstance().getLevelToIconMap();
824 
825       Action action =
826         new AbstractAction(
827           l.toString(), (Icon) levelIconMap.get(l.toString())) {
828           public void actionPerformed(ActionEvent e) {
829             if (r != null) {
830               r.setThreshold(l);
831               updateCurrentlySelectedNodeInDispatchThread();
832             }
833           }
834         };
835 
836       JRadioButtonMenuItem item = new JRadioButtonMenuItem(action);
837       item.setSelected(r.getThreshold() == l);
838 
839       return item;
840     }
841 
842     /**
843      * Builds a relevant set of menus for when the Root node in the Receiver
844      * tree has been selected
845      *
846      */
847     private void buildForReceiversRoot() {
848       JMenuItem startAll = new JMenuItem(startAllAction);
849 
850       add(newReceiverButtonAction);
851 
852       addSeparator();
853       add(startAll);
854     }
855   }
856 
857   /**
858    * A simple Panel that has toolbar buttons for restarting,
859    * playing, pausing, and stoping receivers
860    *
861    * @author Paul Smith &lt;psmith@apache.org&gt;
862    *
863    */
864   private class ReceiverToolbar extends JToolBar
865     implements TreeSelectionListener {
866     final SmallButton newReceiverButton;
867 
868     private ReceiverToolbar() {
869       setFloatable(false);
870 
871       SmallButton restartReceiverButton = new SmallButton(restartReceiverButtonAction);
872       restartReceiverButton.setText(null);
873       
874       SmallButton shutdownReceiverButton =
875         new SmallButton(shutdownReceiverButtonAction);
876       shutdownReceiverButton.setText(null);
877 
878       SmallButton saveReceiversButton =
879         new SmallButton(saveReceiversButtonAction);
880       saveReceiversButton.setText(null);
881 
882       SmallButton restartAllButton = new SmallButton(startAllAction);
883       restartAllButton.setText(null);
884       
885       
886 
887       newReceiverButton = new SmallButton(newReceiverButtonAction);
888       newReceiverButton.setText(null);
889       newReceiverButton.addMouseListener(new PopupListener(newReceiverPopup));
890 
891       add(newReceiverButton);
892       add(restartAllButton);
893 
894       addSeparator();
895 
896       add(restartReceiverButton);
897       add(shutdownReceiverButton);
898       add(saveReceiversButton);
899 
900       addSeparator();
901 
902       Action closeAction =
903         new AbstractAction(null, LineIconFactory.createCloseIcon()) {
904           public void actionPerformed(ActionEvent e) {
905             ReceiversPanel.this.setVisible(false);
906           }
907         };
908 
909       closeAction.putValue(
910         Action.SHORT_DESCRIPTION, "Closes the Receiver panel");
911 
912       add(Box.createHorizontalGlue());
913 
914       add(new SmallButton(closeAction));
915 
916       add(Box.createHorizontalStrut(5));
917     }
918 
919     /**
920      * Ensures the enabled property of the actions is set properly
921      * according to the currently selected node in the tree
922      */
923     public void valueChanged(TreeSelectionEvent e) {
924       updateActions();
925     }
926   }
927 }