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.receivers;
    18
    19  import java.awt.BorderLayout;
    20  import java.awt.Component;
    21  import java.awt.event.WindowAdapter;
    22  import java.awt.event.WindowEvent;
    23  import java.beans.BeanInfo;
    24  import java.beans.IntrospectionException;
    25  import java.beans.Introspector;
    26  import java.beans.PropertyChangeEvent;
    27  import java.beans.PropertyChangeListener;
    28  import java.beans.PropertyDescriptor;
    29  import java.util.ArrayList;
    30  import java.util.Arrays;
    31  import java.util.Collections;
    32  import java.util.Comparator;
    33  import java.util.HashMap;
    34  import java.util.List;
    35  import java.util.Map;
    36
    37  import javax.swing.AbstractCellEditor;
    38  import javax.swing.DefaultCellEditor;
    39  import javax.swing.JFrame;
    40  import javax.swing.JPanel;
    41  import javax.swing.JScrollPane;
    42  import javax.swing.JTable;
    43  import javax.swing.JTextField;
    44  import javax.swing.table.AbstractTableModel;
    45  import javax.swing.table.DefaultTableModel;
    46  import javax.swing.table.TableCellEditor;
    47  import javax.swing.table.TableModel;
    48
    49  import org.apache.log4j.Level;
    50  import org.apache.log4j.LogManager;
    51  import org.apache.log4j.Logger;
    52  import org.apache.log4j.chainsaw.ChainsawConstants;
    53  import org.apache.log4j.chainsaw.Generator;
    54  import org.apache.log4j.chainsaw.helper.TableCellEditorFactory;
    55  import org.apache.log4j.net.SocketHubReceiver;
    56  import org.apache.log4j.plugins.Plugin;
    57
    58
    59  /**
    60   * A panel that allows the user to edit a particular Plugin, by using introspection
    61   * this class discovers the modifiable properties of the Plugin
    62   * @author Paul Smith &lt;psmith@apache.org&gt;
    63   */
    64  public class PluginPropertyEditorPanel extends JPanel {
    65
    66      private final JScrollPane scrollPane = new JScrollPane();
    67      private final JTable propertyTable = new JTable();
    68
    69      private Plugin plugin;
    70      private TableModel defaultModel = new DefaultTableModel(
    71              new String[] { "Property", "Value" }, 1);
    72
    73      private static final Logger logger = LogManager.getLogger(PluginPropertyEditorPanel.class);
    74      /**
    75       *
    76       */
    77      public PluginPropertyEditorPanel() {
    78          super();
    79          initComponents();
    80          setupListeners();
    81      }
    82
    83      /**
    84       *
    85       */
    86      private void initComponents() {
    87          propertyTable.setRowHeight(ChainsawConstants.DEFAULT_ROW_HEIGHT);
    88          setLayout(new BorderLayout());
    89          scrollPane.setViewportView(propertyTable);
    90
    91          add(scrollPane, BorderLayout.CENTER);
    92
    93          propertyTable.setModel(
    94              defaultModel = new DefaultTableModel(
    95                      new String[] { "Property", "Value" }, 1));
    96
    97      }
    98
    99      /**
    100      *
    101      */
    102     private void setupListeners() {
    103         addPropertyChangeListener("plugin", new PropertyChangeListener() {
    104
    105                 public void propertyChange(PropertyChangeEvent evt) {
    106
    107                     final Plugin p = (Plugin) evt.getNewValue();
    108
    109                     if (p != null) {
    110
    111                         try {
    112
    113                             PluginPropertyTableModel model =
    114                                 new PluginPropertyTableModel(p);
    115                             propertyTable.setModel(model);
    116                             propertyTable.getColumnModel().getColumn(1)
    117                             .setCellEditor(new PluginTableCellEditor());
    118                             propertyTable.setEnabled(true);
    119                         } catch (Throwable e) {
    120                             logger.error("Failed to introspect the Plugin", e);
    121                         }
    122                     } else {
    123                         propertyTable.setModel(defaultModel);
    124                         propertyTable.setEnabled(false);
    125                     }
    126
    127                 }
    128             });
    129     }
    130
    131     public static void main(String[] args) {
    132
    133         JFrame frame = new JFrame("Property Editor Test bed");
    134         frame.addWindowListener(new WindowAdapter() {
    135                 public void windowClosed(WindowEvent e) {
    136                     System.exit(1);
    137                 }
    138             });
    139
    140         PluginPropertyEditorPanel panel = new PluginPropertyEditorPanel();
    141
    142
    143         frame.getContentPane().add(panel);
    144         frame.pack();
    145
    146         frame.setVisible(true);
    147
    148         SocketHubReceiver r = new SocketHubReceiver();
    149
    150         panel.setPlugin(r);
    151
    152         try {
    153             Thread.sleep(3000);
    154
    155             panel.setPlugin(new Generator("MyPlugin"));
    156         } catch (Exception e) {
    157             // TODO: handle exception
    158         }
    159
    160
    161     }
    162
    163     /**
    164      * @return Returns the plugin.
    165      */
    166     public final Plugin getPlugin() {
    167
    168         return plugin;
    169     }
    170
    171     /**
    172      * @param plugin The plugin to set.
    173      */
    174     public final void setPlugin(Plugin plugin) {
    175
    176         Plugin oldValue = this.plugin;
    177         this.plugin = plugin;
    178         firePropertyChange("plugin", oldValue, this.plugin);
    179     }
    180
    181     /**
    182      * @author psmith
    183      *
    184      */
    185     private class PluginTableCellEditor extends AbstractCellEditor
    186         implements TableCellEditor {
    187
    188         private Map editorMap = new HashMap();
    189         private DefaultCellEditor defaultEditor = new DefaultCellEditor(
    190                 new JTextField());
    191         private DefaultCellEditor currentEditor = defaultEditor;
    192
    193         private PluginTableCellEditor() {
    194
    195             editorMap.put(Boolean.class,
    196                 TableCellEditorFactory.createBooleanTableCellEditor());
    197             editorMap.put(Level.class,
    198                 TableCellEditorFactory.createLevelTableCellEditor());
    199             //support primitive boolean parameters with the appropriate editor
    200             editorMap.put(boolean.class, TableCellEditorFactory.createBooleanTableCellEditor());
    201         }
    202
    203         /* (non-Javadoc)
    204          * @see javax.swing.table.TableCellEditor#getTableCellEditorComponent(javax.swing.JTable, java.lang.Object, boolean, int, int)
    205          */
    206         public Component getTableCellEditorComponent(JTable table, Object value,
    207             boolean isSelected, int row, int column) {
    208
    209            PluginPropertyTableModel model = (PluginPropertyTableModel) table.getModel();
    210            PropertyDescriptor descriptor =  model.getDescriptors()[row];
    211            Class valueClass = descriptor.getPropertyType();
    212
    213             if (editorMap.containsKey(valueClass)) {
    214
    215                 DefaultCellEditor editor =
    216                     (DefaultCellEditor) editorMap.get(valueClass);
    217                 logger.debug("Located CellEditor for " + valueClass);
    218                 currentEditor = editor;
    219
    220                 return currentEditor.getTableCellEditorComponent(table, value,
    221                     isSelected, row, column);
    222             }
    223
    224             currentEditor = defaultEditor;
    225             logger.debug("Cell value class " + valueClass +
    226                 " not know, using default editor");
    227
    228             return defaultEditor.getTableCellEditorComponent(table, value,
    229                 isSelected, row, column);
    230         }
    231
    232         /* (non-Javadoc)
    233          * @see javax.swing.CellEditor#getCellEditorValue()
    234          */
    235         public Object getCellEditorValue() {
    236
    237             return currentEditor.getCellEditorValue();
    238         }
    239
    240     }
    241
    242     private static class PluginPropertyTableModel extends AbstractTableModel {
    243
    244         private final PropertyDescriptor[] descriptors;
    245         private final Plugin plugin;
    246
    247         private PluginPropertyTableModel(Plugin p)
    248             throws IntrospectionException {
    249             super();
    250
    251             BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
    252
    253             List list = new ArrayList(Arrays.asList(
    254                         beanInfo.getPropertyDescriptors()));
    255
    256             Collections.sort(list, new Comparator() {
    257
    258                     public int compare(Object o1, Object o2) {
    259
    260                         PropertyDescriptor d1 = (PropertyDescriptor) o1;
    261                         PropertyDescriptor d2 = (PropertyDescriptor) o2;
    262
    263                         return d1.getDisplayName().compareToIgnoreCase(
    264                             d2.getDisplayName());
    265                     }
    266                 });
    267             this.plugin = p;
    268             this.descriptors = (PropertyDescriptor[]) list.toArray(
    269                     new PropertyDescriptor[0]);
    270         }
    271
    272         /* (non-Javadoc)
    273          * @see javax.swing.table.AbstractTableModel#getValueAt(int, int)
    274          */
    275         public Object getValueAt(int row, int col) {
    276
    277             PropertyDescriptor d = descriptors[row];
    278
    279             switch (col) {
    280
    281             case 1:
    282
    283                 try {
    284
    285                     Object object = d.getReadMethod().invoke(plugin,
    286                             new Object[0]);
    287
    288                     if (object != null) {
    289
    290                         return object;
    291                     }
    292                 } catch (Exception e) {
    293                     logger.error(
    294                         "Error reading value for PropertyDescriptor " + d);
    295                 }
    296
    297                 return "";
    298
    299             case 0:
    300                 return d.getName();
    301             }
    302
    303             return null;
    304         }
    305
    306         /* (non-Javadoc)
    307          * @see javax.swing.table.AbstractTableModel#getColumnCount()
    308          */
    309         public int getColumnCount() {
    310
    311             return 2;
    312         }
    313
    314         /* (non-Javadoc)
    315          * @see javax.swing.table.AbstractTableModel#getRowCount()
    316          */
    317         public int getRowCount() {
    318
    319             return descriptors.length;
    320         }
    321
    322         /* (non-Javadoc)
    323          * @see javax.swing.table.TableModel#isCellEditable(int, int)
    324          */
    325         public boolean isCellEditable(int rowIndex, int columnIndex) {
    326
    327 //        TODO Determine if the property is one of the ones a User could edit
    328             if (columnIndex == 1) {
    329
    330                 return descriptors[rowIndex].getWriteMethod() != null;
    331             }
    332
    333             return false;
    334         }
    335
    336         /* (non-Javadoc)
    337          * @see javax.swing.table.TableModel#getColumnName(int)
    338          */
    339         public String getColumnName(int column) {
    340
    341             return (column == 0) ? "Property" : "Value";
    342         }
    343
    344         /* (non-Javadoc)
    345          * @see javax.swing.table.TableModel#setValueAt(java.lang.Object, int, int)
    346          */
    347         public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    348
    349
    350             if (columnIndex == 1) {
    351                 //ensure name is set
    352                 if (descriptors[rowIndex].getName().equalsIgnoreCase("name") && (aValue == null || aValue.toString().trim().equals(""))) {
    353                     logger.error("Name required");
    354                     return;
    355                 }
    356                 aValue = translateValueIfNeeded(rowIndex, aValue);
    357                 logger.debug(
    358                     "setValueAt, " + rowIndex + ", " + columnIndex +
    359                     ", value=" + aValue + ", valueClass" + aValue.getClass());
    360
    361                 try {
    362                     descriptors[rowIndex].getWriteMethod().invoke(plugin,
    363                         new Object[] { aValue });
    364                     fireTableCellUpdated(rowIndex, columnIndex);
    365                 } catch (IllegalArgumentException e) {
    366                     // ignore
    367                 } catch (Exception e) {
    368                     logger.error(
    369                         "Failed to modify the Plugin because of Exception", e);
    370                 }
    371
    372             } else {
    373                 super.setValueAt(aValue, rowIndex, columnIndex);
    374             }
    375         }
    376
    377         /**
    378          * @param row
    379          * @param value
    380          * @return
    381          */
    382         private Object translateValueIfNeeded(int row, Object value) {
    383
    384             if ((descriptors[row].getPropertyType() == int.class) ||
    385                     (descriptors[row].getPropertyType() == Integer.class)) {
    386
    387                 try {
    388
    389                     return Integer.valueOf(value.toString());
    390                 } catch (Exception e) {
    391                     logger.error("Failed to convert to Integer type");
    392                 }
    393             }
    394
    395             return value;
    396         }
    397         /**
    398          * @return Returns the descriptors.
    399          */
    400         public final PropertyDescriptor[] getDescriptors() {
    401           return descriptors;
    402         }
    403     }
    404 }