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.messages;
19  
20  import org.apache.log4j.Layout;
21  import org.apache.log4j.Level;
22  import org.apache.log4j.Logger;
23  import org.apache.log4j.TTCCLayout;
24  import org.apache.log4j.chainsaw.ChainsawConstants;
25  import org.apache.log4j.chainsaw.LoggingEventWrapper;
26  import org.apache.log4j.chainsaw.PopupListener;
27  import org.apache.log4j.chainsaw.SmallButton;
28  import org.apache.log4j.chainsaw.icons.ChainsawIcons;
29  import org.apache.log4j.varia.ListModelAppender;
30  
31  import javax.swing.*;
32  import javax.swing.event.ListDataEvent;
33  import javax.swing.event.ListDataListener;
34  import java.awt.*;
35  import java.awt.event.ActionEvent;
36  import java.beans.PropertyChangeSupport;
37  
38  
39  /**
40   * The MessageCenter is central place for all elements within Chainsaw to
41   * notify the user of important information.
42   * <p>
43   * This class uses log4j itself quite significantly.  All user message
44   * are sent to this classes log4j Logger (org.apache.log4j.chainsaw.message.MessageCenter).
45   * <p>
46   * To register a message with the user, you can use the addMessage(String) style methods on
47   * this class, or just as easily, get a handle to this class' logger, and log
48   * it as you would normally do.
49   * <p>
50   * All events to this logger are trapped within a Custom appender (additivity
51   * will be switched OFF), which stores the events in a ListModel.
52   * <p>
53   * You can invoke the setVisible() method to display all the messages
54   *
55   * @author Paul Smith &lt;psmith@apache.org&gt;
56   */
57  public class MessageCenter {
58      private static final MessageCenter instance = new MessageCenter();
59      private final Logger logger = Logger.getLogger(MessageCenter.class);
60      private Layout layout = new TTCCLayout();
61      private final JList<org.apache.log4j.spi.LoggingEvent> messageList = new JList<>();
62      private final ListModelAppender appender = new ListModelAppender();
63      private ListCellRenderer listCellRenderer =
64          new LayoutListCellRenderer(layout);
65      private PropertyChangeSupport propertySupport =
66          new PropertyChangeSupport(this);
67      private JScrollPane pane = new JScrollPane(messageList);
68      private final JToolBar toolbar = new JToolBar();
69      private JPopupMenu popupMenu = new JPopupMenu();
70      private PopupListener popupListener = new PopupListener(popupMenu);
71      private Action clearAction;
72      private final JPanel componentPanel = new JPanel(new BorderLayout());
73  
74      private MessageCenter() {
75          setupActions();
76          setupComponentPanel();
77          setupLogger();
78          setupListeners();
79          setupPopMenu();
80          setupToolbar();
81      }
82  
83      /**
84       *
85       */
86      private void setupPopMenu() {
87          popupMenu.add(clearAction);
88      }
89  
90      /**
91       *
92       */
93      private void setupToolbar() {
94          JButton clearButton = new SmallButton(clearAction);
95          clearButton.setText(null);
96          toolbar.add(clearButton);
97  
98          toolbar.setFloatable(false);
99      }
100 
101     private void setupActions() {
102         clearAction =
103             new AbstractAction("Clear") {
104                 public void actionPerformed(ActionEvent e) {
105                     appender.clearModel();
106                 }
107             };
108         clearAction.putValue(
109             Action.SMALL_ICON, new ImageIcon(ChainsawIcons.DELETE));
110     }
111 
112     private void setupListeners() {
113         propertySupport.addPropertyChangeListener(
114             "layout",
115             evt -> {
116                 Layout newLayout = (Layout) evt.getNewValue();
117                 messageList.setCellRenderer(new LayoutListCellRenderer(newLayout));
118             });
119         messageList.addMouseListener(popupListener);
120 
121         appender.getModel().addListDataListener(
122             new ListDataListener() {
123                 public void contentsChanged(ListDataEvent e) {
124                     updateActions();
125                 }
126 
127                 public void intervalAdded(ListDataEvent e) {
128                     updateActions();
129                 }
130 
131                 public void intervalRemoved(ListDataEvent e) {
132                     updateActions();
133                 }
134             });
135     }
136 
137     /**
138      *
139      */
140     private void updateActions() {
141         clearAction.putValue(
142             "enabled",
143             (appender.getModel().getSize() > 0) ? Boolean.TRUE : Boolean.FALSE);
144     }
145 
146     private void setupLogger() {
147         logger.addAppender(appender);
148         logger.setAdditivity(true);
149         logger.setLevel(Boolean.getBoolean("log4j.debug") ? Level.DEBUG : Level.INFO);
150     }
151 
152     private void setupComponentPanel() {
153         messageList.setModel(appender.getModel());
154         messageList.setCellRenderer(listCellRenderer);
155 
156         componentPanel.add(this.toolbar, BorderLayout.NORTH);
157         componentPanel.add(this.pane, BorderLayout.CENTER);
158     }
159 
160     public final JComponent getGUIComponent() {
161         return componentPanel;
162     }
163 
164     public ListModel<org.apache.log4j.spi.LoggingEvent> getModel() {
165         return messageList.getModel();
166     }
167 
168     public static MessageCenter getInstance() {
169         return instance;
170     }
171 
172     public void addMessage(String message) {
173         logger.info(message);
174     }
175 
176 
177     /**
178      * @return Returns the layout used by the MessageCenter.
179      */
180     public final Layout getLayout() {
181         return layout;
182     }
183 
184     /**
185      * @param layout Sets the layout to be used by the MessageCenter .
186      */
187     public final void setLayout(Layout layout) {
188         Layout oldValue = this.layout;
189         this.layout = layout;
190         propertySupport.firePropertyChange("layout", oldValue, this.layout);
191     }
192 
193     /**
194      * Returns the logger that can be used to log
195      * messages to display within the Message Center.
196      *
197      * @return logger
198      */
199     public final Logger getLogger() {
200         return this.logger;
201     }
202 
203     /**
204      * This class simply renders an event by delegating the effort to a
205      * Log4j layout instance.
206      *
207      * @author Paul Smith &lt;psmith@apache.org&gt;
208      */
209     private static class LayoutListCellRenderer extends DefaultListCellRenderer {
210         private Layout layout;
211 
212         /**
213          * @param layout
214          */
215         public LayoutListCellRenderer(Layout layout) {
216             super();
217             this.layout = layout;
218         }
219 
220         /* (non-Javadoc)
221          * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
222          */
223         public Component getListCellRendererComponent(
224             JList list, Object value, int index, boolean isSelected,
225             boolean cellHasFocus) {
226             value = layout.format(((LoggingEventWrapper) value).getLoggingEvent());
227 
228             Component c =
229                 super.getListCellRendererComponent(
230                     list, value, index, isSelected, cellHasFocus);
231             c.setBackground(
232                 ((index % 2) == 0) ? ChainsawConstants.COLOR_EVEN_ROW_BACKGROUND
233                     : ChainsawConstants.COLOR_ODD_ROW_BACKGROUND);
234 
235             return c;
236         }
237     }
238 }