United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4889282 : java.beans.EventHandler defeats AWT exception handling

Details
Type:
Bug
Submit Date:
2003-07-11
Status:
Resolved
Updated Date:
2003-09-26
Project Name:
JDK
Resolved Date:
2003-09-26
Component:
client-libs
OS:
windows_2000
Sub-Component:
java.beans
CPU:
x86
Priority:
P4
Resolution:
Fixed
Affected Versions:
1.4.1,1.4.2
Fixed Versions:
5.0 (tiger)

Related Reports
Backport:
Duplicate:
Relates:

Sub Tasks

Description
Name: gm110360			Date: 07/11/2003


FULL PRODUCT VERSION :
java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
java.beans.EventHandler.invoke() catches any exceptions and
dumps them to stdout. This defeats any application
exception handling (e.g. via the sun.awt.exception.handler
property).

EventHandler.invoke() implements
java.lang.reflect.InvocationHandler.invoke() which is
declared to throw Throwable, so EventHandler.invoke() could
just rethrow InvocationTargetException.getTargetException()
which would allow AWT (or any other caller) to handle it.

Another alternative would be to allow the application to
set a java.beans.ExceptionListener on the instance returned
by EventHandler.create. EventHandler.invoke could then
notify the listener of any exceptions that occur.

The current behavior makes EventHandler a black hole that
hides exceptions from the caller.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Run the attached ExceptionHandler app
2. Press the "Handled" button - it prints a message saying
the exception was handled - because this class is
registered as the sun.awt.exception.handler and the
exception propagated to the AWT event loop.
3. Press the "Unhandled" button - EventHandler dumps the
stack to stderr. The application was given no chance to
handle the exception.

EXPECTED VERSUS ACTUAL BEHAVIOR :
I would expect EventHandler.invoke to allow the exception
to propagate up to the AWT event loop which would then
handle it.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
This is the output from first pressing the "Handled" button and then
the "Unhandled" button:


caught and handled AWT exception java.lang.RuntimeException: handled
java.lang.RuntimeException: unhandled
	at ExceptionHandler.handleButton(ExceptionHandler.java:32)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at java.beans.EventHandler.invoke(EventHandler.java:377)
	at $Proxy0.actionPerformed(Unknown Source)
	at javax.swing.AbstractButton.fireActionPerformed
(AbstractButton.java:1767)
	at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed
(AbstractButton.java:1820)
	at javax.swing.DefaultButtonModel.fireActionPerformed
(DefaultButtonModel.java:419)
	at javax.swing.DefaultButtonModel.setPressed
(DefaultButtonModel.java:257)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased
(BasicButtonListener.java:258)
	at java.awt.Component.processMouseEvent(Component.java:5021)
	at java.awt.Component.processEvent(Component.java:4818)
	at java.awt.Container.processEvent(Container.java:1525)
	at java.awt.Component.dispatchEventImpl(Component.java:3526)
	at java.awt.Container.dispatchEventImpl(Container.java:1582)
	at java.awt.Component.dispatchEvent(Component.java:3367)
	at java.awt.LightweightDispatcher.retargetMouseEvent
(Container.java:3359)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3074)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3004)
	at java.awt.Container.dispatchEventImpl(Container.java:1568)
	at java.awt.Window.dispatchEventImpl(Window.java:1581)
	at java.awt.Component.dispatchEvent(Component.java:3367)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:445)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy
(EventDispatchThread.java:191)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy
(EventDispatchThread.java:144)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:130)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:98)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;

public class ExceptionHandler {
	public static void main(String args[]) {
		System.setProperty
("sun.awt.exception.handler", "ExceptionHandler");

		JFrame frm = new JFrame();
		frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		JButton button1 = new JButton("Handled");
		button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				throw new RuntimeException("handled");
			}
		});

		JButton button2 = new JButton("Unhandled");
		button2.addActionListener((ActionListener)EventHandler.create
(ActionListener.class,
			new ExceptionHandler(), "handleButton"));

		frm.getContentPane().setLayout(new BorderLayout());
		frm.getContentPane().add(button1, BorderLayout.NORTH);
		frm.getContentPane().add(button2, BorderLayout.SOUTH);
		frm.pack();
		frm.setVisible(true);
	}

	public void handleButton() {
		throw new RuntimeException("unhandled");
	}

	// Signature for sun.awt.exception.handler
	public void handle(Throwable e) {
		System.out.println("caught and handled AWT exception " + e);
	}
}
---------- END SOURCE ----------
(Review ID: 158659) 
======================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
tiger

FIXED IN:
tiger

INTEGRATED IN:
tiger
tiger-b22


                                     
2004-06-14
SUGGESTED FIX

*** /tmp/geta9122	2003-09-12 17:23:34.000000000 -0700
--- EventHandler.java	2003-09-12 17:23:01.000000000 -0700
***************
*** 310,326 ****
                  getter = getMethod(target.getClass(), first, new Class[]{});
              } 
              if (getter == null) { 
!                 System.err.println("No method called: " + first + " defined on " + target); 
!                 return null; 
              } 
              Object newTarget = getter.invoke(target, new Object[]{}); 
              return applyGetters(newTarget, rest); 
          } 
          catch (Throwable e) { 
!             System.out.println(e); 
!             System.err.println("Failed to call method: " + first + " on " + target); 
          }
-         return null; 
      }
      
      /**
--- 310,325 ----
                  getter = getMethod(target.getClass(), first, new Class[]{});
              } 
              if (getter == null) { 
! 		throw new RuntimeException("No method called: " + first + 
! 					   " defined on " + target); 
              } 
              Object newTarget = getter.invoke(target, new Object[]{}); 
              return applyGetters(newTarget, rest); 
          } 
          catch (Throwable e) { 
!             throw new RuntimeException("Failed to call method: " + first + 
! 				       " on " + target, e); 
          }
      }
      
      /**
***************
*** 369,383 ****
                      targetMethod = getMethod(target.getClass(), "set" + capitalize(action), argTypes);
                  }
                  if (targetMethod == null) { 
!                     System.err.println("No target method called: " + action + " defined on class " + target.getClass() + " with argument type " + argTypes[0]); 
                  }
                  return targetMethod.invoke(target, newArgs);
              } 
              catch (IllegalAccessException ex) {
!                 ex.printStackTrace();
              }
              catch (InvocationTargetException ex) {
!                 ex.getTargetException().printStackTrace();
              }
          }
          return null;
--- 368,385 ----
                      targetMethod = getMethod(target.getClass(), "set" + capitalize(action), argTypes);
                  }
                  if (targetMethod == null) { 
! 		    throw new RuntimeException("No method called: " + 
! 					       action + " on class " + 
! 					       target.getClass() + " with argument "
! 					       + argTypes[0]); 
                  }
                  return targetMethod.invoke(target, newArgs);
              } 
              catch (IllegalAccessException ex) {
!                 throw new RuntimeException(ex);
              }
              catch (InvocationTargetException ex) {
!                 throw new RuntimeException(ex.getTargetException());
              }
          }
          return null;
                                     
2004-06-11
EVALUATION

The EventHandler.invoke() method does not declare that it throws Throwable thus changing the semantics of the InvocationHandler interface. 

Not sure what the correct semantics should be. Perhaps it should just pass the exceptions to the caller rather than handle, wrap and throw exceptions that it encounters.

Will fix for Tiger.
###@###.### 2003-08-04

The invoke() method is public but is only called by the dynamic proxy mechanism. Changing the invoke method to match the signature of the InvocationHandler interface fixes the problem with no observable compatiblity issues. 

###@###.### 2003-09-08

We have decided that we should maintain the current interface and throw RuntimeException in the invoke exception handling mechanism.

Also, the applyGetters method will also rethrow the exception since it will return null that will result in context-less NPE in invoke().

I'm also going to fix the case in which targetMethod == null since that case will result in an NPE.
###@###.### 2003-09-12
                                     
2003-09-12



Hardware and Software, Engineered to Work Together