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