【问题标题】:How to choose an AWT-EventQueue thread, when there are several of them当有多个线程时,如何选择一个 AWT-EventQueue 线程
【发布时间】:2015-12-11 15:38:05
【问题描述】:

我使用 DLL 注入和一些 jni 技巧成功地将自己的 Java 代码注入到正在运行的 Oracle Forms 应用程序中。 (Windows 7、32 位、Oracle Forms 11、JRE Java 8)

我能够遍历组件树并在一些基本 Java 对象中查询和设置值,例如来自类 oracle.forms.ui.VTextField 的对象

我在尝试模拟用户点击 oracle.apps.fnd.ui.Button 时卡住了

我尝试了两件事:

  1. 调用AbstractButton类的simulatePush方法
  2. 调用PushButton类的activate方法

(这 2 个类在 Button 的类层次结构中)

结果相同: 1.起初,它工作正常:当按钮是“搜索”按钮时,搜索完成并显示结果。 2. 然后,它立即中断应用程序,并弹出一个说FRM-92100 Your connection to the Server was interrupted

从那里,应用程序被挂起。

更新: 似乎导致与服务器断开连接的错误是:

java.lang.SecurityException: 这个 KeyboardFocusManager 不是 安装在当前线程的上下文中 java.awt.KeyboardFocusManager.checkCurrentKFMSecurity(未知来源) 在 java.awt.KeyboardFocusManager.getGlobalFocusOwner(未知来源) 在 java.awt.KeyboardFocusManager.processSynchronousLightweightTransfer(未知 来源)在 sun.awt.windows.WComponentPeer.processSynchronousLightweightTransfer(本机 方法)在 sun.awt.windows.WComponentPeer.requestFocus(未知 源)在 java.awt.Component.requestFocusHelper(未知源)在 java.awt.Component.requestFocusHelper(未知来源)在 java.awt.Component.requestFocus(未知来源)在 oracle.forms.handler.UICommon.updateFocus(未知来源)在 oracle.forms.handler.UICommon.setFVP(未知来源)在 oracle.forms.handler.UICommon.setFVP(未知来源)在 oracle.forms.handler.UICommon.onUpdate(未知来源)在 oracle.forms.handler.ComponentItem.onUpdate(未知来源)在 oracle.forms.handler.JavaContainer.onUpdate(未知来源)在 oracle.forms.handler.UICommon.onUpdate(未知来源)在 oracle.forms.engine.Runform.onUpdateHandler(未知来源)在 oracle.forms.engine.Runform.processMessage(未知来源)在 oracle.forms.engine.Runform.processSet(未知来源)在 oracle.forms.engine.Runform.onMessageReal(未知来源)在 oracle.forms.engine.Runform.onMessage(未知来源)在 oracle.forms.engine.Runform.processEventEnd(未知来源)在 oracle.ewt.lwAWT.LWComponent.redispatchEvent(未知来源)在 oracle.ewt.lwAWT.LWComponent.processEvent(未知来源)在 oracle.ewt.button.PushButton.activate(未知来源)在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(未知来源)在 java.lang.reflect.Method.invoke(未知来源)在 CustomAWT.run(CustomAWT.java:34) 在 java.awt.event.InvocationEvent.dispatch(未知来源)在 java.awt.EventQueue.dispatchEventImpl(未知来源)在 java.awt.EventQueue.access$400(未知来源)在 java.awt.EventQueue$2.run(Unknown Source) at java.awt.EventQueue$2.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) 在 java.security.AccessControlContext$1.doIntersectionPrivilege(未知 源)在 java.awt.EventQueue.dispatchEvent(未知源)在 java.awt.EventDispatchThread.pumpOneEventForFilters(未知来源) 在 java.awt.EventDispatchThread.pumpEventsForFilter(未知来源) 在 java.awt.EventDispatchThread.pumpEventsForHierarchy(未知 来源)在 java.awt.EventDispatchThread.pumpEvents(未知来源) 在 java.awt.EventDispatchThread.pumpEvents(Unknown Source) 在 java.awt.EventDispatchThread.run(未知来源)

我的代码在这里:CustomAWT.run(CustomAWT.java:34) 并以invokeLater 调用。问题可能是:调用 oracle.ewt.button.PushButton.activate 方法时,我不在正确的 EDT。

在 Java 控制台中使用“列出线程”,我得到:

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 线程...现在的问题是:如何查询/检索正确的线程,以及如何使 Runnable 传递给 invokeLater 运行在“好线程”中(我猜好的是最后一个(AWT-EventQueue-2

【问题讨论】:

  • 你试过separateFrame="True"小程序参数吗?
  • @dan 我无法更改服务器上的任何内容。不确定我是否理解您的建议。 Oracle Forms 应用程序(Oracle Application eBusiness Center)已经作为主窗口运行,是 Window Desktop 的子窗口。

标签: java java-native-interface event-dispatch-thread oracleforms


【解决方案1】:

经过大量实验和谷歌搜索,使用 EventQueueThreadGroup 等关键字,我终于找到了解决方案(请注意,在 为我工作 类别中)。

我使用sun.awt.AppContext 类。一些文档和来源here (grepcode.com)

  1. 使用getAppContexts 方法获取正在运行的AppContext 的集合。
  2. 对于每个检索到的AppContext,使用getThreadGroup 方法获取他的ThreadGroup
  3. 对于ThreadGroup 对象,使用getName 方法。
  4. 当线程组的名称以表单应用程序的 http: 地址开头时,使用 AppContextget 方法检索键名为 sun.awt.AppContext.EVENT_QUEUE_KEYObject 属性。
  5. 检索到的对象是EventQueue。创建一个java.awt.event.InvocationEvent 对象,将您的Runnable 传递给CTOR,并使用EventQueuepostEvent 方法。
  6. 您的run 方法将在正确的线程中执行。

备注:

  • 此答案是一个特定的、适用于我的解决方案,适用于通过 Internet Explorer 链接启动并在 java.exe 进程中运行的 Oracle Forms 应用程序。在这种情况下,3 个线程组如问题所示:mainPlugin Thread Grouphttp://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup 您的里程可能会有所不同。
  • 如果您不使用完全反射,而是使用 import sun.awt.AppContext,编译器可能会发出 warning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release 形式的警告,这不是很酷,但我暂时会接受。李>
  • run方法中,我用oracle.ewt.lwAWT.AbstractButtonsimulatePush方法测试OK。
  • 这里模拟的方法是invokeLater。对于invokeAndWait,需要更多代码围绕postEvent 调用。请参阅 EventQueue 类的一些资源,作为起点。

【讨论】:

    【解决方案2】:

    要获得正确的 EDT 线程,无论您的线程组如何,您都可以使用 SunToolkit.targetToAppContext(Object target),并且对于参数,您可以将其提供给您打算操作的 AWT 组件。示例source

    然后使用EventQueue eq = SunToolkit.getSystemEventQueueImplPP(appContext);获取EventQueue

    最后,使用您的可运行文件创建一个新的 InvocationEvent 并在 EQ 上调用 postEvent。

    【讨论】:

      【解决方案3】:

      你应该能够扩展 VButton 类 你的类定义应该是这样的:

      public class AmazingButton extends VButton implements FocusListener
      

      那么你需要一个init 类,比如:

      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 ;
          }
      
        }
      

      我希望这段代码对你有用。

      问候

      【讨论】:

      • 我不明白为什么这应该是我问题的答案,抱歉。请仔细阅读问题。我的问题是在正确的 EDT 线程中调用 GUI 方法。
      【解决方案4】:

      我使用 DLL 注入和一些 jni 技巧成功地将自己的 Java 代码注入到正在运行的 Oracle Forms 应用程序中。

      才是真正的问题,IMO。

      你正遭受目标固定的困扰,这意味着你,程序员,对他们想要什么样的解决方案有一个固定的想法,这让你对其他一切视而不见。目标锁定导致了飞机失事,因为即使是经验丰富且聪明的飞行员(实际上是整个驾驶舱!)也变得如此专注于一种思维方式中的一个问题,以至于他们让其他灾难溜过。

      摆脱这种心态。

      您想要的解决方案没有奏效,因此请继续尝试其他方法。就像@nightfox79 已经提供给您的明智选项以及它的变体一样。

      您正试图绕过一个复杂的对象类,而您可能应该简单地扩展您试图破解的现有类。这就是 OOP 开发的全部基础。

      DLL/JNI Trickery 在明智的解决方案中没有立足之地,IMO。

      我很同情那些必须维护和修复任何基于 DLL/JNI hack 的代码解决方案的人。那就是疯狂所在。

      您认为invokeLater() 没有在正确的 EDT 下运行的理论可能是错误的。根据文档,invokeLater()始终将您请求的代码排队到 AWT 事件处理程序的待处理代码列表中,这正是它应该在的位置。试图绕过这一点几乎肯定会导致可怕的问题。 invokeLater() 的全部目的是在您调用它的 EDT 中推迟重量级处理,并稍后在完全相同的线程上运行它。这是invokeLater() 中的一个错误,如果不是,IMO。

      但是,如果您希望检查正在运行的线程代码,那么我所知道的唯一测试就是在您的代码中使用它;

      if (SwingUtilities.isEventDispatchThread())
      {
          System.err.println("Is running on EDT");
      }
      else
      {
          System.err.println("Is not running on EDT");
      }
      

      【讨论】:

      • 我无法修改 Oracle Forms 应用程序。当我注入的代码使用involkeLater 时,Runnable 最终在名为AWT-EventQueue-0 的线程中运行。当我将getSystemEventQueuepostEventInvocationEvent 一起使用时也是如此。请参阅问题中的线程列表。我应该使用的 EDT 命名为 AWT-EventQueue-2,这实际上就是我安装 AWTEventListener 时调用的那个。问题实际上是关于如何选择正确的EventQueue/EDT
      • 从本质上讲,您是在试图绕过听起来非常像安全屏障的东西——毕竟您的代码会引发 SecurityException。如果不允许或无法在正确的线程上安装事件侦听器,则您可能根本不应该这样做。您可能应该接受这一点,并为您实际尝试以功能方式提供的任何内容找到不同的解决方案。有时这是唯一的解决方案。
      • 我被允许并且能够安装我自己的AWTEventListener,并且我在AWT-EventQueue-2 Java 线程中正确接收了所有事件。 “模拟按钮操作”的请求来自另一个进程,这是异步的,这就是为什么我需要一种方法来检索良好的 EventQueue(因为如果我只使用getSystemEventQueue 我的Runnable 将在AWT-EventQueue-0 中运行)。
      猜你喜欢
      • 2011-09-19
      • 2013-03-02
      • 2015-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      相关资源
      最近更新 更多