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  package org.apache.log4j.chainsaw;
18  
19  import java.awt.Component;
20  import java.awt.Dimension;
21  import java.awt.GridBagConstraints;
22  import java.awt.GridBagLayout;
23  import java.awt.GridLayout;
24  import java.awt.event.ActionEvent;
25  import java.awt.event.ActionListener;
26  import java.awt.event.FocusEvent;
27  import java.awt.event.FocusListener;
28  import java.io.File;
29  import java.net.MalformedURLException;
30  import java.net.URL;
31  
32  import javax.swing.AbstractAction;
33  import javax.swing.BorderFactory;
34  import javax.swing.Box;
35  import javax.swing.ButtonGroup;
36  import javax.swing.DefaultComboBoxModel;
37  import javax.swing.DefaultListCellRenderer;
38  import javax.swing.JButton;
39  import javax.swing.JCheckBox;
40  import javax.swing.JComboBox;
41  import javax.swing.JFileChooser;
42  import javax.swing.JFrame;
43  import javax.swing.JLabel;
44  import javax.swing.JList;
45  import javax.swing.JPanel;
46  import javax.swing.JRadioButton;
47  import javax.swing.JTextArea;
48  import javax.swing.SwingUtilities;
49  import javax.swing.event.ListDataEvent;
50  import javax.swing.event.ListDataListener;
51  import javax.swing.filechooser.FileFilter;
52  
53  import org.apache.log4j.LogManager;
54  import org.apache.log4j.Logger;
55  import org.apache.log4j.chainsaw.prefs.SettingsManager;
56  import org.apache.log4j.net.PortBased;
57  import org.apache.log4j.net.SocketAppender;
58  import org.apache.log4j.net.SocketHubReceiver;
59  import org.apache.log4j.net.SocketReceiver;
60  
61  
62  /***
63   * A dialog panel to inform the user that they do not have
64   * Receiver's defined, and prompting them to either
65   * load a Log4j Log file, search for a Log4j configuration file
66   * or use the GUI to define the Receivers
67   *
68   * @author Paul Smith
69   */
70  class NoReceiversWarningPanel extends JPanel {
71  
72      private final JComboBox previousConfigs = new JComboBox();
73  
74      private final JRadioButton simpleReceiver = new JRadioButton(
75              "Let me use a simple Receiver:");
76  
77      private final JRadioButton justLoadingFile = new JRadioButton(
78              "I'm fine thanks, don't worry");
79      private final JRadioButton searchOption = new JRadioButton(
80              "Let me search for a configuration file");
81      private final JRadioButton chainsawSavedConfigOption = new JRadioButton(
82              "Load configuration file saved by Chainsaw");
83      private final JRadioButton manualOption = new JRadioButton(
84              "Let me define Receivers manually");
85      private final JButton okButton = new JButton("Ok");
86      private final PanelModel model = new PanelModel();
87      final DefaultComboBoxModel configModel = new DefaultComboBoxModel();
88  
89      final DefaultComboBoxModel simpleReceiverModel = new DefaultComboBoxModel();
90      final DefaultComboBoxModel simplePortModel = new DefaultComboBoxModel();
91  
92      private boolean dontWarnMeAgain = false;
93      
94      private final Logger logger = LogManager.getLogger(NoReceiversWarningPanel.class);
95  
96      NoReceiversWarningPanel() {
97          initComponents();
98      }
99  
100     /***
101      * Returns the current Model/state of the chosen options by the user.
102      * @return
103      */
104     PanelModel getModel() {
105 
106         return model;
107     }
108 
109     /***
110      * Clients of this panel can configure the ActionListener to be used
111      * when the user presses the OK button, so they can read
112      * back this Panel's model top determine what to do.
113      * @param actionListener
114      */
115     void setOkActionListener(ActionListener actionListener) {
116         okButton.addActionListener(actionListener);
117     }
118 
119     /***
120      * Sets up all the GUI components for this paenl
121      */
122     private void initComponents() {
123         setLayout(new GridBagLayout());
124 
125         GridBagConstraints gc = new GridBagConstraints();
126 
127         setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
128 
129         gc.gridx = 1;
130         gc.fill = GridBagConstraints.BOTH;
131         gc.weightx = 1.0;
132         gc.weighty = 1.0;
133 
134         JTextArea label = new JTextArea(
135                 "You will not be able to receive events from a Remote source unless you define one in the Log4J configuration file.\n");
136         label.setWrapStyleWord(true);
137         label.setLineWrap(true);
138         label.setEditable(false);
139         label.setOpaque(false);
140         label.setFont(getFont());
141 
142         add(label, gc);
143 
144         gc.weightx = 0;
145         gc.weighty = 0;
146         gc.gridy = 2;
147         add(Box.createVerticalStrut(20), gc);
148 
149         JPanel optionpanel = new JPanel();
150         optionpanel.setLayout(new GridLayout(5, 1, 3, 3));
151         optionpanel.setBackground(getBackground());
152         optionpanel.setBorder(BorderFactory.createEtchedBorder());
153 
154         final ButtonGroup optionGroup = new ButtonGroup();
155 
156         simpleReceiver.setToolTipText(
157             "Creates one of the standard Receivers on one of the standard port");
158         simpleReceiver.setMnemonic('p');
159 
160         searchOption.setToolTipText(
161             "Allows you to choose a Log4J Configuration file that contains Receiver definitions");
162 
163         searchOption.setMnemonic('S');
164         
165         chainsawSavedConfigOption.setToolTipText(
166              "Allows you to load Receiver definitions saved by Chanisaw previously");
167  
168         chainsawSavedConfigOption.setMnemonic('C');
169 
170 
171         manualOption.setToolTipText(
172             "Opens the Receivers panel so you can define them via a GUI");
173 
174         manualOption.setMnemonic('m');
175 
176         justLoadingFile.setToolTipText(
177             "Use this if you just want to view a Log4J Log file stored somewhere");
178 
179         justLoadingFile.setMnemonic('I');
180 
181 //    searchOption.setOpaque(false);
182         manualOption.setOpaque(false);
183         justLoadingFile.setOpaque(false);
184 
185         optionGroup.add(searchOption);
186         optionGroup.add(chainsawSavedConfigOption);
187         optionGroup.add(manualOption);
188         optionGroup.add(justLoadingFile);
189         optionGroup.add(simpleReceiver);
190 
191         chainsawSavedConfigOption.setEnabled(getModel().isChinsawConfigFileExists());
192 
193         gc.gridy = 3;
194 
195 
196         configModel.removeAllElements();
197 
198         previousConfigs.setModel(configModel);
199         previousConfigs.setOpaque(false);
200         previousConfigs.setBackground(getBackground());
201         previousConfigs.setToolTipText(
202             "Previously loaded configurations can be chosen here");
203 
204         previousConfigs.setEditable(true);
205 
206         previousConfigs.getModel().addListDataListener(new ListDataListener() {
207                 private void validateUrl() {
208                     okButton.setEnabled(isValidConfigURL());
209                 }
210 
211                 public void contentsChanged(ListDataEvent e) {
212                     validateUrl();
213                 }
214 
215                 public void intervalAdded(ListDataEvent e) {
216                     validateUrl();
217                 }
218 
219                 public void intervalRemoved(ListDataEvent e) {
220                     validateUrl();
221                 }
222             });
223 
224         previousConfigs.setMaximumSize(new Dimension(200,
225                 (int) previousConfigs.getPreferredSize().getHeight()));
226         previousConfigs.setPreferredSize(previousConfigs.getMaximumSize());
227         previousConfigs.getEditor().getEditorComponent().addFocusListener(
228             new FocusListener() {
229                 public void focusGained(FocusEvent e) {
230                     selectAll();
231                 }
232 
233                 private void selectAll() {
234                     previousConfigs.getEditor().selectAll();
235                 }
236 
237                 public void focusLost(FocusEvent e) {
238                 }
239             });
240 
241         final JButton searchButton = new JButton(new AbstractAction(
242                     "Browse...") {
243                     public void actionPerformed(ActionEvent e) {
244 
245                         try {
246 
247                             URL url = browseForConfig();
248 
249                             if (url != null) {
250                                 getModel().configUrl = url;
251                                 configModel.addElement(url);
252                                 previousConfigs.getModel().setSelectedItem(
253                                     url);
254                             }
255                         } catch (Exception ex) {
256                             logger.error(
257                                 "Error browswing for Configuration file", ex);
258                         }
259                     }
260                 });
261 
262         searchButton.setToolTipText(
263             "Shows a File Open dialog to allow you to find a configuration file");
264 
265 
266         simplePortModel.addElement(new PortBased() {
267 
268                 private void unsupported() {
269                     throw new UnsupportedOperationException(
270                         "Should not be used in this context");
271                 }
272 
273                 public String getName() {
274                     unsupported();
275 
276                     return null;
277                 }
278 
279                 public boolean isActive() {
280                     unsupported();
281 
282                     return false;
283                 }
284 
285                 public int getPort() {
286 
287                     return 4445;
288                 }
289 
290                 public String toString() {
291 
292                     return getPort() + " (Old style/standard Chainsaw port)";
293                 }
294             });
295 
296         simplePortModel.addElement(new PortBased() {
297 
298                 private void unsupported() {
299                     throw new UnsupportedOperationException(
300                         "Should not be used in this context");
301                 }
302 
303                 public String getName() {
304                     unsupported();
305 
306                     return null;
307                 }
308 
309                 public boolean isActive() {
310                     unsupported();
311 
312                     return false;
313                 }
314 
315                 public int getPort() {
316 
317                     return SocketAppender.DEFAULT_PORT;
318                 }
319 
320                 public String toString() {
321 
322                     return getPort() + " (Default SocketAppender port)";
323                 }
324             });
325 
326         JPanel simpleSocketPanel = new JPanel(new GridBagLayout());
327 
328         GridBagConstraints simpleSocketGC = new GridBagConstraints();
329 
330         simpleSocketPanel.add(simpleReceiver, simpleSocketGC);
331 
332         final JComboBox socketCombo = new JComboBox(simplePortModel);
333 
334 
335         simpleReceiverModel.addElement(SocketReceiver.class);
336         simpleReceiverModel.addElement(SocketHubReceiver.class);
337 
338         final JComboBox receiverCombo = new JComboBox(simpleReceiverModel);
339         receiverCombo.setEditable(false);
340         receiverCombo.setRenderer(new DefaultListCellRenderer() {
341                 public Component getListCellRendererComponent(JList list,
342                     Object value, int index, boolean isSelected,
343                     boolean cellHasFocus) {
344 
345                     Component c = super.getListCellRendererComponent(list,
346                             value, index, isSelected, cellHasFocus);
347 
348                     if (value instanceof Class) {
349 
350                         Class receiverClass = (Class) value;
351                         JLabel cellLabel = (JLabel) c;
352                         String shortenedName = receiverClass.getName()
353                             .substring(
354                                 receiverClass.getName().lastIndexOf('.') + 1);
355                         cellLabel.setText(shortenedName);
356                     }
357 
358                     return c;
359                 }
360 
361             });
362 
363         simpleSocketPanel.add(receiverCombo);
364         simpleSocketPanel.add(new JLabel(" on port "));
365         simpleSocketPanel.add(socketCombo, simpleSocketGC);
366 
367         /***
368          * This listener activates/deactivates certain controls based on the current
369          * state of the options
370          */
371         ActionListener al = new ActionListener() {
372                 public void actionPerformed(ActionEvent e) {
373                     previousConfigs.setEnabled(e.getSource() == searchOption);
374                     searchButton.setEnabled(e.getSource() == searchOption);
375                     socketCombo.setEnabled(e.getSource() == simpleReceiver);
376                     receiverCombo.setEnabled(e.getSource() == simpleReceiver);
377 
378                     if (optionGroup.isSelected(searchOption.getModel())) {
379                         okButton.setEnabled(isValidConfigURL());
380                     } else {
381                         okButton.setEnabled(true);
382                     }
383                 }
384             };
385 
386         searchOption.addActionListener(al);
387         chainsawSavedConfigOption.addActionListener(al);
388         manualOption.addActionListener(al);
389         justLoadingFile.addActionListener(al);
390         simpleReceiver.addActionListener(al);
391 
392         justLoadingFile.doClick();
393 
394         JPanel searchOptionPanel = new JPanel(new GridBagLayout());
395 
396         searchOptionPanel.setOpaque(false);
397 
398         GridBagConstraints searchGCC = new GridBagConstraints();
399 
400         searchGCC.fill = GridBagConstraints.HORIZONTAL;
401         searchGCC.gridx = 1;
402         searchGCC.weightx = 0.0;
403         searchGCC.anchor = GridBagConstraints.WEST;
404         searchOptionPanel.add(searchOption, searchGCC);
405 
406         searchGCC.fill = GridBagConstraints.NONE;
407         searchGCC.weightx = 1.0;
408         searchGCC.gridx = 2;
409         searchOptionPanel.add(Box.createHorizontalStrut(5), searchGCC);
410 
411         searchGCC.gridx = 3;
412         searchGCC.weightx = 0.0;
413         searchOptionPanel.add(previousConfigs, searchGCC);
414 
415         searchGCC.weightx = 0.0;
416         searchGCC.gridx = 4;
417         searchOptionPanel.add(Box.createHorizontalStrut(5), searchGCC);
418         searchGCC.gridx = 5;
419         searchOptionPanel.add(searchButton, searchGCC);
420 
421 //    searchGCC.gridx = 6;
422 //    searchGCC.fill = GridBagConstraints.HORIZONTAL;
423 //    searchGCC.weightx = 1.0;
424 //    searchOptionPanel.add(Box.createHorizontalGlue(), searchGCC);
425 
426 
427         optionpanel.add(justLoadingFile);
428         optionpanel.add(simpleSocketPanel);
429         optionpanel.add(searchOptionPanel);
430         optionpanel.add(chainsawSavedConfigOption);
431         optionpanel.add(manualOption);
432 
433         add(optionpanel, gc);
434 
435         gc.gridy = GridBagConstraints.RELATIVE;
436         gc.weightx = 0;
437         gc.fill = GridBagConstraints.NONE;
438         gc.anchor = GridBagConstraints.SOUTHEAST;
439 
440         add(Box.createVerticalStrut(20), gc);
441 
442         okButton.setMnemonic('O');
443 
444         final JCheckBox dontwarnIfNoReceiver = new JCheckBox(
445                 "Don't show me this again");
446         JPanel okPanel = new JPanel();
447 
448         okPanel.add(dontwarnIfNoReceiver);
449         okPanel.add(okButton);
450         add(okPanel, gc);
451 
452         okButton.addActionListener(new ActionListener() {
453 
454                 public void actionPerformed(ActionEvent e) {
455                     dontWarnMeAgain = dontwarnIfNoReceiver.isSelected();
456                 }
457             });
458     }
459 
460     /***
461      * Returns the URL chosen by the user for a Configuration file
462      * or null if they cancelled.
463      */
464     private URL browseForConfig() throws MalformedURLException {
465 
466         JFileChooser chooser = new JFileChooser();
467         chooser.setDialogTitle("Search for Log4j configuration...");
468         chooser.setDialogType(JFileChooser.OPEN_DIALOG);
469         chooser.setFileFilter(new FileFilter() {
470                 public boolean accept(File f) {
471 
472                     return f.isDirectory() ||
473                     f.getName().endsWith(".properties") ||
474                     f.getName().endsWith(".xml");
475                 }
476 
477                 public String getDescription() {
478 
479                     return "Log4j Configuration file";
480                 }
481             });
482 
483         chooser.showOpenDialog(this);
484 
485         File selectedFile = chooser.getSelectedFile();
486 
487         if (selectedFile == null) {
488 
489             return null;
490         }
491 
492         if (!selectedFile.exists() || !selectedFile.canRead()) {
493 
494             return null;
495         }
496 
497         return chooser.getSelectedFile().toURI().toURL();
498     }
499 
500     /***
501      * Determions if the Configuration URL is a valid url.
502      */
503     private boolean isValidConfigURL() {
504 
505         if (previousConfigs.getSelectedItem() == null) {
506 
507             return false;
508         }
509 
510         String urlString = previousConfigs.getSelectedItem().toString();
511 
512         try {
513             getModel().configUrl = new URL(urlString);
514 
515             return true;
516         } catch (Exception ex) {
517         }
518 
519         return false;
520     }
521 
522     public static void main(String[] args) {
523 
524         JFrame frame = new JFrame();
525         frame.getContentPane().add(new NoReceiversWarningPanel());
526         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
527         frame.pack();
528         frame.setVisible(true);
529     }
530 
531     /***
532      * @return Returns the dontWarnMeAgain.
533      */
534     public final boolean isDontWarnMeAgain() {
535 
536         return dontWarnMeAgain;
537     }
538 
539     /***
540      * This class represents the model of the chosen options the user
541      * has configured.
542      *
543      */
544     class PanelModel {
545 
546         private URL configUrl;
547         private File file;
548 
549         public PanelModel(){
550             file = new File(SettingsManager.getInstance().getSettingsDirectory(), "receiver-configs.xml");
551         }
552 
553         boolean isLoadLogFile() {
554 
555             return justLoadingFile.isSelected();
556         }
557 
558         boolean isSimpleReceiverMode() {
559 
560             return simpleReceiver.isSelected();
561         }
562 
563         int getSimplePort() {
564 
565             return ((PortBased) simplePortModel.getSelectedItem()).getPort();
566         }
567 
568         Class getSimpleReceiverClass() {
569 
570             return (Class) simpleReceiverModel.getSelectedItem();
571         }
572 
573         boolean isLoadConfig() {
574 
575             return searchOption.isSelected();
576         }
577 
578         boolean isLoadSavedConfigs() {
579 
580             return chainsawSavedConfigOption.isSelected();
581         }
582 
583         boolean isManualMode() {
584 
585             return manualOption.isSelected();
586         }
587 
588         public Object[] getRememberedConfigs() {
589 
590             Object[] urls = new Object[configModel.getSize()];
591 
592             for (int i = 0; i < configModel.getSize(); i++) {
593                 urls[i] = configModel.getElementAt(i);
594             }
595 
596             return urls;
597         }
598 
599         public void setRememberedConfigs(final Object[] configs) {
600             SwingUtilities.invokeLater(new Runnable() {
601                     public void run() {
602                         configModel.removeAllElements();
603 
604                         for (int i = 0; i < configs.length; i++) {
605                             configModel.addElement(configs[i]);
606                         }
607                     }
608                 });
609         }
610 
611         URL getConfigToLoad() {
612 
613             return configUrl;
614         }
615 
616         boolean isChinsawConfigFileExists(){
617 
618             return file.exists();
619 
620         }
621 
622         URL getSavedConfigToLoad() {
623             try {
624                 if (file.exists()){
625                     return file.toURL();
626                 } else {
627                     logger.debug("No configuration file found");
628                 }
629             } catch (MalformedURLException e) {
630                 logger.error("Error laoding saved configurations by Chainsaw", e);
631             }
632             return null;
633         }
634     }
635 }