当有多个 AWT-EventQueue 线程时如何选择
How to choose an AWT-EventQueue thread, when there are several of them
我使用 DLL 注入和一些 jni 技巧成功地将我自己的 Java 代码注入到 运行ning Oracle Forms 应用程序中。 (Windows 7、32 位,Oracle Forms 11,JRE Java 8)
我能够遍历组件树并在一些基本 Java 对象中查询和设置值,例如来自 class oracle.forms.ui.VTextField
的对象
我在尝试模拟用户点击 oracle.apps.fnd.ui.Button
时卡住了
我尝试了两件事:
- 调用
AbstractButton
的simulatePush
方法class
- 调用
PushButton
class 的activate
方法
(2 classes 在 Button
的 class 层次结构中)
结果相同:
1. 起初,它工作正常:当按钮是 "Search" 按钮时,搜索完成并显示结果。
2. 然后,它立即中断应用程序,并弹出一个窗口说 FRM-92100 Your connection to the Server was interrupted
.
从那里,应用程序被挂起。
更新:
似乎导致与服务器断开连接的错误是:
java.lang.SecurityException: this KeyboardFocusManager is not
installed in the current thread's context at
java.awt.KeyboardFocusManager.checkCurrentKFMSecurity(Unknown Source)
at java.awt.KeyboardFocusManager.getGlobalFocusOwner(Unknown Source)
at
java.awt.KeyboardFocusManager.processSynchronousLightweightTransfer(Unknown
Source) at
sun.awt.windows.WComponentPeer.processSynchronousLightweightTransfer(Native
Method) at sun.awt.windows.WComponentPeer.requestFocus(Unknown
Source) at java.awt.Component.requestFocusHelper(Unknown Source) at
java.awt.Component.requestFocusHelper(Unknown Source) at
java.awt.Component.requestFocus(Unknown Source) at
oracle.forms.handler.UICommon.updateFocus(Unknown Source) at
oracle.forms.handler.UICommon.setFVP(Unknown Source) at
oracle.forms.handler.UICommon.setFVP(Unknown Source) at
oracle.forms.handler.UICommon.onUpdate(Unknown Source) at
oracle.forms.handler.ComponentItem.onUpdate(Unknown Source) at
oracle.forms.handler.JavaContainer.onUpdate(Unknown Source) at
oracle.forms.handler.UICommon.onUpdate(Unknown Source) at
oracle.forms.engine.Runform.onUpdateHandler(Unknown Source) at
oracle.forms.engine.Runform.processMessage(Unknown Source) at
oracle.forms.engine.Runform.processSet(Unknown Source) at
oracle.forms.engine.Runform.onMessageReal(Unknown Source) at
oracle.forms.engine.Runform.onMessage(Unknown Source) at
oracle.forms.engine.Runform.processEventEnd(Unknown Source) at
oracle.ewt.lwAWT.LWComponent.redispatchEvent(Unknown Source) at
oracle.ewt.lwAWT.LWComponent.processEvent(Unknown Source) at
oracle.ewt.button.PushButton.activate(Unknown Source) at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at
java.lang.reflect.Method.invoke(Unknown Source) at
CustomAWT.run(CustomAWT.java:34) at
java.awt.event.InvocationEvent.dispatch(Unknown Source) at
java.awt.EventQueue.dispatchEventImpl(Unknown Source) at
java.awt.EventQueue.access0(Unknown Source) at
java.awt.EventQueue.run(Unknown Source) at
java.awt.EventQueue.run(Unknown Source) at
java.security.AccessController.doPrivileged(Native Method) at
java.security.AccessControlContext.doIntersectionPrivilege(Unknown
Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at
java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown
Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at
java.awt.EventDispatchThread.run(Unknown Source)
我的代码在这里:CustomAWT.run(CustomAWT.java:34)
并用 invokeLater
调用。问题可能是:调用 oracle.ewt.button.PushButton.activate
方法时,我不在正确的 EDT 中。
在 Java 控制台中使用 "List Threads",我得到:
Dump thread list ...
Group main,ac=30,agc=2,pri=10
main,5,alive
traceMsgQueueThread,5,alive,daemon
Timer-0,5,alive
Java Plug-In Pipe Worker Thread (Client-Side),5,alive,daemon
AWT-Shutdown,5,alive
AWT-Windows,6,alive,daemon
AWT-EventQueue-0,6,alive
SysExecutionTheadCreator,5,alive,daemon
CacheMemoryCleanUpThread,5,alive,daemon
CacheCleanUpThread,5,alive,daemon
Browser Side Object Cleanup Thread,5,alive
JVM[id=0]-Heartbeat,5,alive,daemon
Windows Tray Icon Thread,5,alive
Thread-13,5,alive
Group Plugin Thread Group,ac=3,agc=0,pri=10
AWT-EventQueue-1,6,alive
TimerQueue,5,alive,daemon
ConsoleWriterThread,6,alive,daemon
Group http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup,ac=13,agc=0,pri=4
Applet 1 LiveConnect Worker Thread,4,alive
AWT-EventQueue-2,4,alive
thread applet-oracle/apps/fnd/formsClient/FormsLauncher.class-1,4,alive
Applet 2 LiveConnect Worker Thread,4,alive
thread applet-oracle.forms.engine.Main-2,4,alive
Forms-StreamMessageReader,4,alive
Forms-StreamMessageWriter,4,alive
HeartBeat,4,alive
Busy indicator,1,alive,daemon
TaskScheduler timer,4,alive
CursorIdler,4,alive
Thread-14,4,alive
Flush Queue,4,alive
Done.
所以,有 三个 AWT-EventQueue
个线程...现在的问题是:如何 query/retrieve 正确的线程,以及如何使 Runnable
在"Good Thread"传给invokeLater
传给运行(我猜好的是最后一个(AWT-EventQueue-2
)
您应该能够扩展 VButton class
您的 class 定义应该是这样的:
public class AmazingButton extends VButton implements FocusListener
那么你需要一个 init
class 比如:
public void init(IHandler handler)
{
m_handler = handler;
super.init(handler);
addMouseListener(new ButtonMouseAdapter());
addFocusListener(this);
}
然后你需要实现监听器并在其中做一些事情:
public void focusGained(FocusEvent e)
{
if (e.getComponent() == this)
{
// put the focus on the component
e.getComponent().requestFocus();
bFocus = true ;
}
}
public void focusLost(FocusEvent e)
{
bFocus = false ;
}
/**
* Private class to handle user mouse actions
*/
class ButtonMouseAdapter extends MouseAdapter
{
/**
* User moved the mouse over the button
*/
public void mouseEntered(MouseEvent me)
{
bFocus=true ;
mouseON();
}
/**
* User moved the mouse out of the button
*/
public void mouseExited(MouseEvent me)
{
bFocus=false ;
mouseOFF();
}
/**
* User moved the mouse out of the button
*/
public void mousePressed(MouseEvent me)
{
bPressed = true ;
}
/**
* User moved the mouse out of the button
*/
public void mouseReleased(MouseEvent me)
{
bPressed = false ;
}
}
希望这段代码对你有用。
此致
I successfully injected my own Java code in a running Oracle Forms application, using DLL Injection and some jni trickery.
才是真正的问题,IMO。
您患有目标固定,这意味着您,程序员,对他们想要什么样的解决方案有着固定的想法,这使您对其他一切视而不见。目标固定导致了飞机失事,因为即使是经验丰富且聪明的飞行员(实际上是整个驾驶舱!)也会以一种心态专注于一个问题,以至于他们让其他灾难从身边溜走。
摆脱这种心态。
您想要的解决方案没有奏效,所以请继续尝试其他方法。就像@nightfox79 已经向您提供的明智选项及其变体。
您正试图绕过一个复杂的对象 class,而您可能应该只是扩展现有的 class,而您正试图绕过它。这就是 OOP 开发的全部基础。
DLL/JNI IMO,诡计在明智的解决方案中没有立足之地。
我很同情那些必须维护和修复任何基于 DLL/JNI 黑客攻击的代码解决方案的人。疯狂的谎言就是这样。
你认为 invokeLater()
不是 运行ning 在正确的 EDT 下的理论可能是错误的。根据文档,invokeLater()
将总是 将您请求的代码排队到 AWT 事件处理程序的待处理代码列表中,这正是它应该在的位置。试图绕过它几乎肯定会导致可怕的问题。 invokeLater()
的全部目的是推迟您从中调用它的 EDT 中的重量级处理,运行 它稍后在完全相同的线程上。这是 invokeLater()
中的错误,如果不是,IMO。
但是,如果您想检查 运行ning 中的线程代码,那么我所知道的唯一测试就是在您的代码中使用它;
if (SwingUtilities.isEventDispatchThread())
{
System.err.println("Is running on EDT");
}
else
{
System.err.println("Is not running on EDT");
}
经过大量实验和 google 使用 EventQueue
和 ThreadGroup
等关键字进行搜索后,我终于找到了解决方案(在 Works For Me 类别,请注意)。
我用的是sun.awt.AppContext
class。一些文档和来源 here (grepcode.com)
- 使用
getAppContexts
方法获取 运行 AppContext
的集合。
- 对于每个检索到的
AppContext
,使用 getThreadGroup
方法获取他的 ThreadGroup
。
- 对于
ThreadGroup
对象,使用getName
方法。
- 当线程组的名称以您的表单应用程序的 http: 地址开头时,检索
Object
属性 键名 sun.awt.AppContext.EVENT_QUEUE_KEY
,使用 get
AppContext
的方法。
- 检索到的对象是
EventQueue
。创建一个 java.awt.event.InvocationEvent
对象,将你的 Runnable
传递给 CTOR,并使用 EventQueue
. 的 postEvent
方法
- 您的
run
方法将在正确的线程中执行。
备注:
- 这个答案是一个特定的、对我有用的解决方案,适用于通过 Internet Explorer link 和 运行 在 java.exe 进程中启动的 Oracle Forms 应用程序。在那种情况下,问题中显示了 3 个线程组:
main
、Plugin Thread Group
和 http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup
您的里程可能会有所不同。
- 如果你不使用全反射,而是导入
sun.awt.AppContext
,编译器可能会以 warning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release
的形式发出警告,这不是很酷,但我会接受,因为暂时.
- 在
run
方法中,我用oracle.ewt.lwAWT.AbstractButton
的simulatePush
方法测试OK。
- 这里模拟的方法是
invokeLater
。对于 invokeAndWait
,postEvent
调用需要更多代码。查看 EventQueue
class 的一些来源作为起点。
要获得正确的 EDT 线程,而不管您的线程组如何,您可以使用 SunToolkit.targetToAppContext(Object target)
,并且对于参数,您可以将您打算操作的 AWT 组件提供给它。示例 source.
然后使用EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
获取EventQueue
最后,使用您的可运行对象创建一个新的 InvocationEvent 并在 EQ 上调用 postEvent。
我使用 DLL 注入和一些 jni 技巧成功地将我自己的 Java 代码注入到 运行ning Oracle Forms 应用程序中。 (Windows 7、32 位,Oracle Forms 11,JRE Java 8)
我能够遍历组件树并在一些基本 Java 对象中查询和设置值,例如来自 class oracle.forms.ui.VTextField
我在尝试模拟用户点击 oracle.apps.fnd.ui.Button
我尝试了两件事:
- 调用
AbstractButton
的simulatePush
方法class - 调用
PushButton
class 的
activate
方法
(2 classes 在 Button
的 class 层次结构中)
结果相同:
1. 起初,它工作正常:当按钮是 "Search" 按钮时,搜索完成并显示结果。
2. 然后,它立即中断应用程序,并弹出一个窗口说 FRM-92100 Your connection to the Server was interrupted
.
从那里,应用程序被挂起。
更新: 似乎导致与服务器断开连接的错误是:
java.lang.SecurityException: this KeyboardFocusManager is not installed in the current thread's context at java.awt.KeyboardFocusManager.checkCurrentKFMSecurity(Unknown Source) at java.awt.KeyboardFocusManager.getGlobalFocusOwner(Unknown Source) at java.awt.KeyboardFocusManager.processSynchronousLightweightTransfer(Unknown Source) at sun.awt.windows.WComponentPeer.processSynchronousLightweightTransfer(Native Method) at sun.awt.windows.WComponentPeer.requestFocus(Unknown Source) at java.awt.Component.requestFocusHelper(Unknown Source) at java.awt.Component.requestFocusHelper(Unknown Source) at java.awt.Component.requestFocus(Unknown Source) at oracle.forms.handler.UICommon.updateFocus(Unknown Source) at oracle.forms.handler.UICommon.setFVP(Unknown Source) at oracle.forms.handler.UICommon.setFVP(Unknown Source) at oracle.forms.handler.UICommon.onUpdate(Unknown Source) at oracle.forms.handler.ComponentItem.onUpdate(Unknown Source) at oracle.forms.handler.JavaContainer.onUpdate(Unknown Source) at oracle.forms.handler.UICommon.onUpdate(Unknown Source) at oracle.forms.engine.Runform.onUpdateHandler(Unknown Source) at oracle.forms.engine.Runform.processMessage(Unknown Source) at oracle.forms.engine.Runform.processSet(Unknown Source) at oracle.forms.engine.Runform.onMessageReal(Unknown Source) at oracle.forms.engine.Runform.onMessage(Unknown Source) at oracle.forms.engine.Runform.processEventEnd(Unknown Source) at oracle.ewt.lwAWT.LWComponent.redispatchEvent(Unknown Source) at oracle.ewt.lwAWT.LWComponent.processEvent(Unknown Source) at oracle.ewt.button.PushButton.activate(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at CustomAWT.run(CustomAWT.java:34) at java.awt.event.InvocationEvent.dispatch(Unknown Source) at java.awt.EventQueue.dispatchEventImpl(Unknown Source) at java.awt.EventQueue.access0(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.awt.EventQueue.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.AccessControlContext.doIntersectionPrivilege(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)
我的代码在这里:CustomAWT.run(CustomAWT.java:34)
并用 invokeLater
调用。问题可能是:调用 oracle.ewt.button.PushButton.activate
方法时,我不在正确的 EDT 中。
在 Java 控制台中使用 "List Threads",我得到:
Dump thread list ...
Group main,ac=30,agc=2,pri=10
main,5,alive
traceMsgQueueThread,5,alive,daemon
Timer-0,5,alive
Java Plug-In Pipe Worker Thread (Client-Side),5,alive,daemon
AWT-Shutdown,5,alive
AWT-Windows,6,alive,daemon
AWT-EventQueue-0,6,alive
SysExecutionTheadCreator,5,alive,daemon
CacheMemoryCleanUpThread,5,alive,daemon
CacheCleanUpThread,5,alive,daemon
Browser Side Object Cleanup Thread,5,alive
JVM[id=0]-Heartbeat,5,alive,daemon
Windows Tray Icon Thread,5,alive
Thread-13,5,alive
Group Plugin Thread Group,ac=3,agc=0,pri=10
AWT-EventQueue-1,6,alive
TimerQueue,5,alive,daemon
ConsoleWriterThread,6,alive,daemon
Group http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup,ac=13,agc=0,pri=4
Applet 1 LiveConnect Worker Thread,4,alive
AWT-EventQueue-2,4,alive
thread applet-oracle/apps/fnd/formsClient/FormsLauncher.class-1,4,alive
Applet 2 LiveConnect Worker Thread,4,alive
thread applet-oracle.forms.engine.Main-2,4,alive
Forms-StreamMessageReader,4,alive
Forms-StreamMessageWriter,4,alive
HeartBeat,4,alive
Busy indicator,1,alive,daemon
TaskScheduler timer,4,alive
CursorIdler,4,alive
Thread-14,4,alive
Flush Queue,4,alive
Done.
所以,有 三个 AWT-EventQueue
个线程...现在的问题是:如何 query/retrieve 正确的线程,以及如何使 Runnable
在"Good Thread"传给invokeLater
传给运行(我猜好的是最后一个(AWT-EventQueue-2
)
您应该能够扩展 VButton class 您的 class 定义应该是这样的:
public class AmazingButton extends VButton implements FocusListener
那么你需要一个 init
class 比如:
public void init(IHandler handler)
{
m_handler = handler;
super.init(handler);
addMouseListener(new ButtonMouseAdapter());
addFocusListener(this);
}
然后你需要实现监听器并在其中做一些事情:
public void focusGained(FocusEvent e)
{
if (e.getComponent() == this)
{
// put the focus on the component
e.getComponent().requestFocus();
bFocus = true ;
}
}
public void focusLost(FocusEvent e)
{
bFocus = false ;
}
/**
* Private class to handle user mouse actions
*/
class ButtonMouseAdapter extends MouseAdapter
{
/**
* User moved the mouse over the button
*/
public void mouseEntered(MouseEvent me)
{
bFocus=true ;
mouseON();
}
/**
* User moved the mouse out of the button
*/
public void mouseExited(MouseEvent me)
{
bFocus=false ;
mouseOFF();
}
/**
* User moved the mouse out of the button
*/
public void mousePressed(MouseEvent me)
{
bPressed = true ;
}
/**
* User moved the mouse out of the button
*/
public void mouseReleased(MouseEvent me)
{
bPressed = false ;
}
}
希望这段代码对你有用。
此致
I successfully injected my own Java code in a running Oracle Forms application, using DLL Injection and some jni trickery.
才是真正的问题,IMO。
您患有目标固定,这意味着您,程序员,对他们想要什么样的解决方案有着固定的想法,这使您对其他一切视而不见。目标固定导致了飞机失事,因为即使是经验丰富且聪明的飞行员(实际上是整个驾驶舱!)也会以一种心态专注于一个问题,以至于他们让其他灾难从身边溜走。
摆脱这种心态。
您想要的解决方案没有奏效,所以请继续尝试其他方法。就像@nightfox79 已经向您提供的明智选项及其变体。
您正试图绕过一个复杂的对象 class,而您可能应该只是扩展现有的 class,而您正试图绕过它。这就是 OOP 开发的全部基础。
DLL/JNI IMO,诡计在明智的解决方案中没有立足之地。
我很同情那些必须维护和修复任何基于 DLL/JNI 黑客攻击的代码解决方案的人。疯狂的谎言就是这样。
你认为 invokeLater()
不是 运行ning 在正确的 EDT 下的理论可能是错误的。根据文档,invokeLater()
将总是 将您请求的代码排队到 AWT 事件处理程序的待处理代码列表中,这正是它应该在的位置。试图绕过它几乎肯定会导致可怕的问题。 invokeLater()
的全部目的是推迟您从中调用它的 EDT 中的重量级处理,运行 它稍后在完全相同的线程上。这是 invokeLater()
中的错误,如果不是,IMO。
但是,如果您想检查 运行ning 中的线程代码,那么我所知道的唯一测试就是在您的代码中使用它;
if (SwingUtilities.isEventDispatchThread())
{
System.err.println("Is running on EDT");
}
else
{
System.err.println("Is not running on EDT");
}
经过大量实验和 google 使用 EventQueue
和 ThreadGroup
等关键字进行搜索后,我终于找到了解决方案(在 Works For Me 类别,请注意)。
我用的是sun.awt.AppContext
class。一些文档和来源 here (grepcode.com)
- 使用
getAppContexts
方法获取 运行AppContext
的集合。 - 对于每个检索到的
AppContext
,使用getThreadGroup
方法获取他的ThreadGroup
。 - 对于
ThreadGroup
对象,使用getName
方法。 - 当线程组的名称以您的表单应用程序的 http: 地址开头时,检索
Object
属性 键名sun.awt.AppContext.EVENT_QUEUE_KEY
,使用get
AppContext
的方法。 - 检索到的对象是
EventQueue
。创建一个java.awt.event.InvocationEvent
对象,将你的Runnable
传递给 CTOR,并使用EventQueue
. 的 - 您的
run
方法将在正确的线程中执行。
postEvent
方法
备注:
- 这个答案是一个特定的、对我有用的解决方案,适用于通过 Internet Explorer link 和 运行 在 java.exe 进程中启动的 Oracle Forms 应用程序。在那种情况下,问题中显示了 3 个线程组:
main
、Plugin Thread Group
和http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup
您的里程可能会有所不同。 - 如果你不使用全反射,而是导入
sun.awt.AppContext
,编译器可能会以warning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release
的形式发出警告,这不是很酷,但我会接受,因为暂时. - 在
run
方法中,我用oracle.ewt.lwAWT.AbstractButton
的simulatePush
方法测试OK。 - 这里模拟的方法是
invokeLater
。对于invokeAndWait
,postEvent
调用需要更多代码。查看EventQueue
class 的一些来源作为起点。
要获得正确的 EDT 线程,而不管您的线程组如何,您可以使用 SunToolkit.targetToAppContext(Object target)
,并且对于参数,您可以将您打算操作的 AWT 组件提供给它。示例 source.
然后使用EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);
最后,使用您的可运行对象创建一个新的 InvocationEvent 并在 EQ 上调用 postEvent。