I want to use custom events, but want to avoid implementing my own event listener management (and event firing) logic, because common wisdom has it, that there are “threading issues” which make that rather difficult. Well, so let’s try Apache’s EventListenerSupport class.
But all I can manage to get is java.lang.reflect.UndeclaredThrowableException
. I have Java 21.0.4 and commons-lang3-3.17.0.jar.
The principal source code is here. It does not contain anything unusual, I think.
import org.apache.commons.lang3.event.EventListenerSupport;
public class MyEventSource {
private EventListenerSupport<MyListener> myListeners = EventListenerSupport
.create(MyListener.class);
public void addMyListener(MyListener listener) {
myListeners.addListener(listener);
public void removeMyListener(MyListener listener) {
myListeners.addListener(listener);
public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
myListeners.fire().handleMyEvent(e);
<code>package demo;
import org.apache.commons.lang3.event.EventListenerSupport;
public class MyEventSource {
private EventListenerSupport<MyListener> myListeners = EventListenerSupport
.create(MyListener.class);
public void addMyListener(MyListener listener) {
myListeners.addListener(listener);
}
public void removeMyListener(MyListener listener) {
myListeners.addListener(listener);
}
public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
myListeners.fire().handleMyEvent(e);
}
}
</code>
package demo;
import org.apache.commons.lang3.event.EventListenerSupport;
public class MyEventSource {
private EventListenerSupport<MyListener> myListeners = EventListenerSupport
.create(MyListener.class);
public void addMyListener(MyListener listener) {
myListeners.addListener(listener);
}
public void removeMyListener(MyListener listener) {
myListeners.addListener(listener);
}
public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
myListeners.fire().handleMyEvent(e);
}
}
The other rather obvious code follows:
public class MyEventMain {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
<code>package demo;
public class MyEventMain {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
}
}
</code>
package demo;
public class MyEventMain {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
}
}
public class MyListenerImpl implements MyListener {
public void handleMyEvent(MyEvent e) {
System.out.println("My event: " + e.getMessage());
<code>package demo;
public class MyListenerImpl implements MyListener {
@Override
public void handleMyEvent(MyEvent e) {
System.out.println("My event: " + e.getMessage());
}
}
</code>
package demo;
public class MyListenerImpl implements MyListener {
@Override
public void handleMyEvent(MyEvent e) {
System.out.println("My event: " + e.getMessage());
}
}
public MyEvent( String m) {
public String getMessage() {
<code>package demo;
public class MyEvent {
private String message;
public MyEvent( String m) {
message = m;
}
public String getMessage() {
return message;
}
}
</code>
package demo;
public class MyEvent {
private String message;
public MyEvent( String m) {
message = m;
}
public String getMessage() {
return message;
}
}
import java.util.EventListener;
interface MyListener extends EventListener {
void handleMyEvent(MyEvent e);
<code>package demo;
import java.util.EventListener;
interface MyListener extends EventListener {
void handleMyEvent(MyEvent e);
}
</code>
package demo;
import java.util.EventListener;
interface MyListener extends EventListener {
void handleMyEvent(MyEvent e);
}
And finally, here is the (slightly edited) stack trace:
<code>java -cp .:/path/to/commons-lang3-3.17.0.jar demo.MyEventMain
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.main(MyEventMain.java:10)
Caused by: java.lang.IllegalAccessException:
class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler
cannot access a member of interface demo.MyListener
with modifiers "public abstract"
java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714)
at java.base/java.lang.reflect.Method.invoke(Method.java:571)
at org.apache.commons.lang3.event.EventListenerSupport$
ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
<code>java -cp .:/path/to/commons-lang3-3.17.0.jar demo.MyEventMain
Start MyEventMain
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.main(MyEventMain.java:10)
Caused by: java.lang.IllegalAccessException:
class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler
cannot access a member of interface demo.MyListener
with modifiers "public abstract"
at
java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(
Reflection.java:394)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714)
at java.base/java.lang.reflect.Method.invoke(Method.java:571)
at org.apache.commons.lang3.event.EventListenerSupport$
ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
... 3 more
</code>
java -cp .:/path/to/commons-lang3-3.17.0.jar demo.MyEventMain
Start MyEventMain
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.main(MyEventMain.java:10)
Caused by: java.lang.IllegalAccessException:
class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler
cannot access a member of interface demo.MyListener
with modifiers "public abstract"
at
java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(
Reflection.java:394)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:714)
at java.base/java.lang.reflect.Method.invoke(Method.java:571)
at org.apache.commons.lang3.event.EventListenerSupport$
ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
... 3 more
Why can the ProxyInvocationHandler
not access that “public abstract” method of my demo.MyListener
interface?
Does anyone see, how this can be made to work? Thanks.
I found a lot of theoretical background about UndeclaredThrowableException
, but none gave me any indication what I might have overlooked.
EDIT 1:
Just changed my MyEventMain
class to this:
import javax.swing.SwingUtilities;
public class MyEventMain implements Runnable {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
SwingUtilities.invokeLater(new MyEventMain());
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
<code>package demo;
import javax.swing.SwingUtilities;
public class MyEventMain implements Runnable {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
SwingUtilities.invokeLater(new MyEventMain());
}
@Override
public void run() {
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
}
}
</code>
package demo;
import javax.swing.SwingUtilities;
public class MyEventMain implements Runnable {
public static void main(String[] args) {
System.out.println("Start MyEventMain");
SwingUtilities.invokeLater(new MyEventMain());
}
@Override
public void run() {
MyEventSource mySource = new MyEventSource();
mySource.addMyListener(new MyListenerImpl());
mySource.someMethodThatFiresEvent();
}
}
The stack trace is of course slightly different but the thrown exception remains the same.
<code>Exception in thread "AWT-EventQueue-0" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.run(MyEventMain.java:16)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.IllegalAccessException: class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler cannot access a member of interface demo.MyListener with modifiers "public abstract"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
at java.base/java.lang.reflect.Method.invoke(Method.java:560)
at org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
<code>Exception in thread "AWT-EventQueue-0" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.run(MyEventMain.java:16)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.IllegalAccessException: class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler cannot access a member of interface demo.MyListener with modifiers "public abstract"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
at java.base/java.lang.reflect.Method.invoke(Method.java:560)
at org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
... 16 more
</code>
Exception in thread "AWT-EventQueue-0" java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.handleMyEvent(Unknown Source)
at demo.MyEventSource.someMethodThatFiresEvent(MyEventSource.java:19)
at demo.MyEventMain.run(MyEventMain.java:16)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.IllegalAccessException: class org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler cannot access a member of interface demo.MyListener with modifiers "public abstract"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
at java.base/java.lang.reflect.Method.invoke(Method.java:560)
at org.apache.commons.lang3.event.EventListenerSupport$ProxyInvocationHandler.invoke(EventListenerSupport.java:127)
... 16 more
EDIT 2:
The following hack works beautifully. Just change someMethodThatFiresEvent
into this:
<code> public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
// The following does not work.
// myListeners.fire().handleMyEvent(e);
for (MyListener listener : myListeners.getListeners()) {
listener.handleMyEvent(e);
<code> public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
// The following does not work.
// myListeners.fire().handleMyEvent(e);
// This works...
for (MyListener listener : myListeners.getListeners()) {
listener.handleMyEvent(e);
}
}
</code>
public void someMethodThatFiresEvent() {
MyEvent e = new MyEvent("Cool");
// The following does not work.
// myListeners.fire().handleMyEvent(e);
// This works...
for (MyListener listener : myListeners.getListeners()) {
listener.handleMyEvent(e);
}
}
I hope it is not less thread safe than Apache’s code.