-
Enhancement
-
Resolution: Not an Issue
-
P4
-
None
-
7
-
x86
-
solaris_8
A DESCRIPTION OF THE REQUEST :
When the SwingWorker.get() method is called it can throw either an java.lang.InterruptedException or an java.util.concurrent.ExecutionException.
Unfortunately, SwingWorker is using java.util.concurrent.FutureTask which catches Throwable objects (Errors and Exceptions).
This results in Errors such as OutOfMemoryError being hidden (normally these would/should not be caught). The only way to determine if an Error occurred is to catch the ExecutionException and check to see if the cause is an Error.
JUSTIFICATION :
I don't believe Errors should be hidden from the application - these are generally serous problems that cannot be recovered from.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would expect only Exceptions to be caught, not Errors (not the Throwable super class).
ACTUAL -
All Errors are caught.
---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.SwingWorker;
/**
*
* @author david.bigham, Ebor Computing
* Last modified by $Author$
* @version $Revision$, $Date$
*/
public class SwingWorkerOOMDialogTest extends javax.swing.JDialog {
/** Logging class. */
private static final String CLASS_NAME = SwingWorkerOOMDialogTest.class.getName();
private static final Logger LOGGER = Logger.getLogger( CLASS_NAME );
private static final int MBYTE = 1024 * 1024;
/** Creates new form SwingWorkerOOMDialogTest */
public SwingWorkerOOMDialogTest(java.awt.Frame parent, boolean modal)
{
super(parent, modal);
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
runButton = new JButton();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
closeDialog(evt);
}
});
runButton.setText("Run");
runButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
runButtonActionPerformed(evt);
}
});
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(runButton, GroupLayout.PREFERRED_SIZE, 67, GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(runButton)
.addContainerGap())
);
pack();
}// </editor-fold>
private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {
SwingWorker<List<byte[]>,Object> swingWorker = new SwingWorker<List<byte[]>,Object>()
{
@Override
protected List<byte[]> doInBackground() throws Exception
{
List<byte[]> byteList = new ArrayList<byte[]>();
boolean isFinished = false;
int totalBytes = 0;
while(!isFinished)
{
byte[] tempData = new byte[MBYTE];
Arrays.fill(tempData, (byte)1);
byteList.add(tempData);
totalBytes += tempData.length;
LOGGER.log(Level.INFO, "Added [" + (tempData.length / MBYTE) + "] MB of data to List. Total [" + (totalBytes / MBYTE) + "] MB");
}
return byteList;
}
@Override
protected void done()
{
try
{
//get the data
List<byte[]> data = get();
LOGGER.log(Level.INFO, "Retrieved [" + (data != null ? data.size() : "NULL") + "] byte arrays of [" + MBYTE + "] length of data.");
}
catch(Exception ex)
{
LOGGER.log(Level.WARNING, "Exception caught in done()", ex);
if(ex.getCause() instanceof Error)
{
LOGGER.log(Level.SEVERE, "#### SwingWorker has wrapped Error in Exception ####", ex.getCause());
}
}
finally
{
runButton.setEnabled(true);
}
}
};
runButton.setEnabled(false);
//start our worker thread
swingWorker.execute();
}
/** Closes the dialog */
private void closeDialog(java.awt.event.WindowEvent evt) {
setVisible(false);
dispose();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
SwingWorkerOOMDialogTest dialog = new SwingWorkerOOMDialogTest(new javax.swing.JFrame(), true);
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
dialog.setVisible(true);
}
});
}
// Variables declaration - do not modify
private JButton runButton;
// End of variables declaration
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Catch the java.util.concurrent.ExecutionException and check to see if the cause is an Error
When the SwingWorker.get() method is called it can throw either an java.lang.InterruptedException or an java.util.concurrent.ExecutionException.
Unfortunately, SwingWorker is using java.util.concurrent.FutureTask which catches Throwable objects (Errors and Exceptions).
This results in Errors such as OutOfMemoryError being hidden (normally these would/should not be caught). The only way to determine if an Error occurred is to catch the ExecutionException and check to see if the cause is an Error.
JUSTIFICATION :
I don't believe Errors should be hidden from the application - these are generally serous problems that cannot be recovered from.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would expect only Exceptions to be caught, not Errors (not the Throwable super class).
ACTUAL -
All Errors are caught.
---------- BEGIN SOURCE ----------
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.SwingWorker;
/**
*
* @author david.bigham, Ebor Computing
* Last modified by $Author$
* @version $Revision$, $Date$
*/
public class SwingWorkerOOMDialogTest extends javax.swing.JDialog {
/** Logging class. */
private static final String CLASS_NAME = SwingWorkerOOMDialogTest.class.getName();
private static final Logger LOGGER = Logger.getLogger( CLASS_NAME );
private static final int MBYTE = 1024 * 1024;
/** Creates new form SwingWorkerOOMDialogTest */
public SwingWorkerOOMDialogTest(java.awt.Frame parent, boolean modal)
{
super(parent, modal);
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
runButton = new JButton();
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
closeDialog(evt);
}
});
runButton.setText("Run");
runButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
runButtonActionPerformed(evt);
}
});
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(runButton, GroupLayout.PREFERRED_SIZE, 67, GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(runButton)
.addContainerGap())
);
pack();
}// </editor-fold>
private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {
SwingWorker<List<byte[]>,Object> swingWorker = new SwingWorker<List<byte[]>,Object>()
{
@Override
protected List<byte[]> doInBackground() throws Exception
{
List<byte[]> byteList = new ArrayList<byte[]>();
boolean isFinished = false;
int totalBytes = 0;
while(!isFinished)
{
byte[] tempData = new byte[MBYTE];
Arrays.fill(tempData, (byte)1);
byteList.add(tempData);
totalBytes += tempData.length;
LOGGER.log(Level.INFO, "Added [" + (tempData.length / MBYTE) + "] MB of data to List. Total [" + (totalBytes / MBYTE) + "] MB");
}
return byteList;
}
@Override
protected void done()
{
try
{
//get the data
List<byte[]> data = get();
LOGGER.log(Level.INFO, "Retrieved [" + (data != null ? data.size() : "NULL") + "] byte arrays of [" + MBYTE + "] length of data.");
}
catch(Exception ex)
{
LOGGER.log(Level.WARNING, "Exception caught in done()", ex);
if(ex.getCause() instanceof Error)
{
LOGGER.log(Level.SEVERE, "#### SwingWorker has wrapped Error in Exception ####", ex.getCause());
}
}
finally
{
runButton.setEnabled(true);
}
}
};
runButton.setEnabled(false);
//start our worker thread
swingWorker.execute();
}
/** Closes the dialog */
private void closeDialog(java.awt.event.WindowEvent evt) {
setVisible(false);
dispose();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
SwingWorkerOOMDialogTest dialog = new SwingWorkerOOMDialogTest(new javax.swing.JFrame(), true);
dialog.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
dialog.setVisible(true);
}
});
}
// Variables declaration - do not modify
private JButton runButton;
// End of variables declaration
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Catch the java.util.concurrent.ExecutionException and check to see if the cause is an Error