-
Bug
-
Resolution: Cannot Reproduce
-
P3
-
7u75, 8, 10, 11, 12
-
x86_64
-
generic
ADDITIONAL SYSTEM INFORMATION :
Java 7 to Java 12 (tested 7u80,8u101,8u181,10.0.2, 12 build 11 (2018/9/13))
A DESCRIPTION OF THE PROBLEM :
If a component called JComponent.revalidate() in non-EDT, then removed from GUI, revalidateRunnableScheduled will not reset, preventing further revalidate call from non-EDT forever, even the component add back to GUI.
Problem because of the event scheduled in JComponent.revalidate() will be removed when the component removed from GUI, and revalidateRunnableScheduled cannot set back to false forever.
The problem should be afterJDK-6459213 : Optimize revalidate. Java 7u55 still not have revalidateRunnableScheduled in JComponent.revalidate() so it works.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(Please check the source code part)
Click "Bug", then "Add". The table will not show the newly added rows. Click "Revalidate" will be able to show back the rows.
(If cannot reproduce, click "Bug" a few more times to trigger the bug)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Click "Add" should always show the newly added rows.
ACTUAL -
Does not show the rows.
---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TableRevalidateTest extends JFrame
{
private ExecutorService services = Executors.newFixedThreadPool(1);
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
TableRevalidateTest frame = new TableRevalidateTest();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
public TableRevalidateTest()
{
super("Table Revalidate Test");
setLocation(500, 200);
setSize(500, 350);
setContentPane(new JPanel(new BorderLayout()));
JButton buttonA = new JButton("Bug"); // Add row in non-EDT and re-add table
JButton buttonB = new JButton("Add"); // Add row in non-EDT
JButton buttonC = new JButton("Revalidate"); // Revalidate in EDT
JButton buttonD = new JButton("Clear"); // Clear rows
final DefaultTableModel tableModel = new DefaultTableModel(new String[] { "Action", "ID" }, 0);
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet())
if (String.valueOf(entry.getKey()).contains("version"))
tableModel.addRow(new Object[] { String.valueOf(entry.getKey()), String.valueOf(entry.getValue()) });
final JTable table = new JTable(tableModel);
final JScrollPane tablePane = new JScrollPane(table);
JPanel northPanel = new JPanel(new GridLayout(1, 0));
northPanel.add(buttonA);
northPanel.add(buttonB);
northPanel.add(buttonC);
northPanel.add(buttonD);
getContentPane().add(northPanel, BorderLayout.NORTH);
getContentPane().add(tablePane, BorderLayout.CENTER);
// Listeners
final AtomicInteger id = new AtomicInteger();
buttonA.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
services.submit(new Runnable()
{
@Override
public void run()
{
tableModel.addRow(new Object[] { "Bug", String.valueOf(id.getAndIncrement()) });
}
});
getContentPane().remove(tablePane);
getContentPane().add(tablePane, BorderLayout.CENTER);
}
});
buttonB.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
services.submit(new Runnable()
{
@Override
public void run()
{
tableModel.addRow(new Object[] { "Add", String.valueOf(id.getAndIncrement()) });
}
});
}
});
buttonC.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
table.revalidate();
}
});
buttonD.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
while (tableModel.getRowCount() > 0)
tableModel.removeRow(tableModel.getRowCount() - 1);
}
});
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Currently no workaround as the component corrupted forever.
FREQUENCY : always
Java 7 to Java 12 (tested 7u80,8u101,8u181,10.0.2, 12 build 11 (2018/9/13))
A DESCRIPTION OF THE PROBLEM :
If a component called JComponent.revalidate() in non-EDT, then removed from GUI, revalidateRunnableScheduled will not reset, preventing further revalidate call from non-EDT forever, even the component add back to GUI.
Problem because of the event scheduled in JComponent.revalidate() will be removed when the component removed from GUI, and revalidateRunnableScheduled cannot set back to false forever.
The problem should be after
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
(Please check the source code part)
Click "Bug", then "Add". The table will not show the newly added rows. Click "Revalidate" will be able to show back the rows.
(If cannot reproduce, click "Bug" a few more times to trigger the bug)
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Click "Add" should always show the newly added rows.
ACTUAL -
Does not show the rows.
---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TableRevalidateTest extends JFrame
{
private ExecutorService services = Executors.newFixedThreadPool(1);
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
TableRevalidateTest frame = new TableRevalidateTest();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
}
public TableRevalidateTest()
{
super("Table Revalidate Test");
setLocation(500, 200);
setSize(500, 350);
setContentPane(new JPanel(new BorderLayout()));
JButton buttonA = new JButton("Bug"); // Add row in non-EDT and re-add table
JButton buttonB = new JButton("Add"); // Add row in non-EDT
JButton buttonC = new JButton("Revalidate"); // Revalidate in EDT
JButton buttonD = new JButton("Clear"); // Clear rows
final DefaultTableModel tableModel = new DefaultTableModel(new String[] { "Action", "ID" }, 0);
for (Map.Entry<Object, Object> entry : System.getProperties().entrySet())
if (String.valueOf(entry.getKey()).contains("version"))
tableModel.addRow(new Object[] { String.valueOf(entry.getKey()), String.valueOf(entry.getValue()) });
final JTable table = new JTable(tableModel);
final JScrollPane tablePane = new JScrollPane(table);
JPanel northPanel = new JPanel(new GridLayout(1, 0));
northPanel.add(buttonA);
northPanel.add(buttonB);
northPanel.add(buttonC);
northPanel.add(buttonD);
getContentPane().add(northPanel, BorderLayout.NORTH);
getContentPane().add(tablePane, BorderLayout.CENTER);
// Listeners
final AtomicInteger id = new AtomicInteger();
buttonA.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
services.submit(new Runnable()
{
@Override
public void run()
{
tableModel.addRow(new Object[] { "Bug", String.valueOf(id.getAndIncrement()) });
}
});
getContentPane().remove(tablePane);
getContentPane().add(tablePane, BorderLayout.CENTER);
}
});
buttonB.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
services.submit(new Runnable()
{
@Override
public void run()
{
tableModel.addRow(new Object[] { "Add", String.valueOf(id.getAndIncrement()) });
}
});
}
});
buttonC.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
table.revalidate();
}
});
buttonD.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
while (tableModel.getRowCount() > 0)
tableModel.removeRow(tableModel.getRowCount() - 1);
}
});
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Currently no workaround as the component corrupted forever.
FREQUENCY : always