JDK-4889282 : java.beans.EventHandler defeats AWT exception handling
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.1,1.4.2
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2003-07-11
  • Updated: 2003-09-26
  • Resolved: 2003-09-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
1.4.2_14Fixed
Related Reports
Duplicate :  
Relates :  
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
14-06-2004

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;
11-06-2004

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
12-09-2003