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