I'm having problems with the selection handler for a JTable.
The table is updated every 15th second from a refresh thread in the Control.
I want to select a row in my JTable and extract a column contents that I will use to build a filename from.
As long as row 0 is not selected during a refresh everything works ok. But if row 0 is selected when the refresh is triggered it looks like it jumps between the setAppserverData procedure and the event handler until it exceeds the rowCount for the table and I get an IndexOutOfBoundsException.
I'm a Java newbee and this is on the edge of my knowledge, so I'm having a hard time figuring out what is wrong with the code. My gut feeling is that it has something to do with me trying to achieve a MVC structure and I fail to separate the code in the correct way.
// The controller
// Appserver worker
class AppserverWorker extends SwingWorker<Integer, Integer>{
protected Integer doInBackground() throws Exception{
// Check appservers
while(true){
theModel.readAppservData();
// Update action buttons
try {
if(!theModel.isStatusOk()){
theGui.actionButtonPanel.setAppServBtnColor("RED");
// theGui.actionButtonPanel.setButtonFlashOn();
}else theGui.actionButtonPanel.setAppServBtnColor("GREEN");
} catch (IllegalArgumentException e1) {
sysLogger.logMsg("SEVERE",e1.getMessage());
JOptionPane.showMessageDialog(null, e1.getMessage());
}
// Update GUI
theGui.extAppServPanel.setAppserverData(theModel.getAppservData());
// Sleep refresh time
// TODO read refresh from init table
try {
Thread.sleep(appServRefreshTime);
} catch (InterruptedException e) {}
}
}
// List selection handler
class SharedListSelectionHandler implements ListSelectionListener {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()){ // To avoid double trigger of listener
System.out.println(">>>>>>>>> Selection list handler >>>>>>>>");
String fileName = null;
String fileExt = ".txt";
ListSelectionModel lsm = (ListSelectionModel)e.getSource();
try {
int selectedRow = lsm.getAnchorSelectionIndex(); // Get the row clicked
if (selectedRow>=0){ // If -1 no row selected
fileName = theGui.extAppServPanel.getAppservName(selectedRow)+fileExt; // Get appserver name and build file name
theModel.readCollectFile(InitParameters.collectFilePath, fileName); // Read collect file
theGui.extAppServPanel.setAppservInfo(theModel.getCollectFileContent()); // Fill the text panel with collect file
}
}
catch(FileNotFoundException e1){
sysLogger.logMsg("SEVERE",this.getClass().getSimpleName()+": Collect file "+fileName+" is missing");
JOptionPane.showMessageDialog(null, "Collect file "+fileName+" is missing");
}
catch(IOException e1){
sysLogger.logMsg("SEVERE",this.getClass().getSimpleName()+": System error: An I/O error occurred");
System.err.println("System error: An I/O error occurred");
System.exit(0);;
}
}
}
}
// The view
public class ExtAppServPanel extends JPanel {
private JTable table;
private DefaultTableModel model;
private JTextArea textAreaInfo;
private JButton btnSaveInfo;
private List colData;
public boolean ignoreTableChanges = false;
/**
* Constructor
*/
public ExtAppServPanel() {
System.out.println(this.getClass().getSimpleName()+": Constructor");
setLayout(new MigLayout("", "[350:376.00,grow,leading]", "[100px:100,grow][][48.00,grow][]"));
this.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Application servers info", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
// Set up table
String[] columnNames = {"Server name", "Type","Status"};
model = new DefaultTableModel(null,columnNames);
table = new JTable(model){
// Color code rows
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
Component comp = super.prepareRenderer(renderer, row, col);
if (!isRowSelected(row)){
comp.setBackground(getBackground());
int modelRow = convertRowIndexToModel(row);
String type = (String)getModel().getValueAt(modelRow, 2);
if ("FAIL".equals(type)) comp.setBackground(Color.RED);
if ("WARNING".equals(type)) comp.setBackground(Color.YELLOW);
}
return comp;
}
};
// Set grid
table.setGridColor(Color.LIGHT_GRAY);
// Set width and alignment
table.getColumnModel().getColumn(1).setMinWidth(200);
table.getColumnModel().getColumn(1).setMaxWidth(200);
table.getColumnModel().getColumn(1).setPreferredWidth(200);
table.getColumnModel().getColumn(2).setMinWidth(75);
table.getColumnModel().getColumn(2).setMaxWidth(75);
table.getColumnModel().getColumn(2).setPreferredWidth(75);
// Add scrollpane
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
table.setFillsViewportHeight(true);
this.add(scrollPane, "cell 0 0,grow");
// Set up text area
JLabel lblDetails = new JLabel("Details");
this.add(lblDetails, "cell 0 1,alignx leading");
textAreaInfo = new JTextArea();
textAreaInfo.setBackground(Color.BLACK);
textAreaInfo.setForeground(Color.GREEN);
textAreaInfo.setEditable(false);
textAreaInfo.setFont(new Font("Lucida Console",Font.PLAIN,10));
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setViewportView(textAreaInfo);
this.add(scrollPane_1, "cell 0 2,grow");
// Save button
btnSaveInfo = new JButton("Save");
add(btnSaveInfo, "cell 0 3,alignx right");
}
/*
* Setters and getters
*/
public void setAppserverData(ArrayList<ArrayList<String>> dataRecord) {
ArrayList<String> dataCol = new ArrayList<String>();
// Init values array from result set
try {
model.setRowCount(0); // <<<<<<<<<< Problem
String[] arrayRow;
String status = "Ok";
// Get first row from list
dataCol = dataRecord.get(0);
arrayRow = new String[dataCol.size()];
arrayRow[0] = dataCol.get(2);
arrayRow[1] = dataCol.get(1);
arrayRow[2] = dataCol.get(4);
for (int i = 0; i < dataRecord.size(); i++){
dataCol = dataRecord.get(i);
// Check status
if (dataCol.get(4).toUpperCase().equals("FAIL")){
status = "FAIL";
}else if (dataCol.get(4).toUpperCase().equals("WARNING")){
status = "WARNING";
}
if (!dataCol.get(2).toUpperCase().equals(arrayRow[0].toUpperCase())){
arrayRow[2] = status; // Override status
System.out.println(">>>>>>>>> Updating table >>>>>>>>");
model.addRow(arrayRow); // Add row to table
arrayRow = new String[dataCol.size()];
arrayRow[0] = dataCol.get(2);
arrayRow[1] = dataCol.get(1);
arrayRow[2] = dataCol.get(4);
status = "Ok";
}
}
setAppservName();
} catch (Exception e) {
e.printStackTrace();
}
}
// Get row count
public int getRowCount(){
return model.getRowCount();
}
// Set table data
public void setAppservName(){
Vector data = model.getDataVector();
Vector row = (Vector) data.elementAt(1);
// Copy the first column
int mColIndex = 0;-
colData = new ArrayList(table.getRowCount()+1);
for (int i = 0; i < table.getRowCount(); i++) {
row = (Vector) data.elementAt(i);
colData.add(row.get(mColIndex));
}
}
// Get table data
public String getAppservName(int rowNum){
return (String) colData.get(rowNum);
}
// Set appserver info
public void setAppservInfo(String appservInfo){
textAreaInfo.setText(appservInfo);
textAreaInfo.setCaretPosition(0); // Set cursor at top of text
}
// Get appserver info
public String getAppservInfo(){
return textAreaInfo.getText();
}
/**
* Action listeners
*/
public void addTableRowListener(ListSelectionListener listSelectionEvent) {
table.getSelectionModel().addListSelectionListener(listSelectionEvent);
}
public void addButtonListener(ActionListener buttonEvent) {
btnSaveInfo.addActionListener(buttonEvent);
btnSaveInfo.setActionCommand("saveAppServInfo");
}
}
Trace output when a non zero row is selected and the refresh thread is run
** Appserver worker **
CmtModel: readAppservData
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>> Rowcount = 8
Trace output when the zero row is selected and the refresh thread is run
** Appserver worker **
CmtModel: readAppservData
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 0 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 1 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 2 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 3 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 4 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 5 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 6 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 7 <<
>>>>>>>>> Updating the table >>>>>>>>
>>>>>>>>> Selection list handler ! >>>>>>>>
>> Controll: selectedRow = 8 <<
java.lang.IndexOutOfBoundsException: Index: 8, Size: 8
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at panels.ExtAppServPanel.getAppservName(ExtAppServPanel.java:219)
at Control$SharedListSelectionHandler.valueChanged(Control.java:330)
at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
at javax.swing.DefaultListSelectionModel.fireValueChanged(Unknown Source)
at javax.swing.DefaultListSelectionModel.insertIndexInterval(Unknown Source)
at javax.swing.JTable.tableRowsInserted(Unknown Source)
at javax.swing.JTable.tableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableRowsInserted(Unknown Source)
at javax.swing.table.DefaultTableModel.insertRow(Unknown Source)
at javax.swing.table.DefaultTableModel.addRow(Unknown Source)
at javax.swing.table.DefaultTableModel.addRow(Unknown Source)
at panels.ExtAppServPanel.setAppserverData(ExtAppServPanel.java:172)
at Control$AppserverWorker.doInBackground(Control.java:434)
at Control$AppserverWorker.doInBackground(Control.java:1)
at javax.swing.SwingWorker$1.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at javax.swing.SwingWorker.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
// Update GUI
theGui.extAppServPanel.setAppserverData(theModel.getAppservData());
Don't update the model or the GUI in the doInBackground()
method.
All updates to the GUI should be done on the EventDispatchThread (EDT)
.
Instead when the data changes you need to "publish" the results so the code can be executed in the process(...)
method of the SwingWorker
which does execute on the EDT and therefore the GUI will be updated on the EDT.
Read the section from the Swing tutorial on Concurrency for more information. The section on Tasks That Have Intermediate Results
has an example of the publish approach.