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