Search code examples
javajava-native-interfaceevent-dispatch-threadoracleforms

How to choose an AWT-EventQueue thread, when there are several of them


I successfully injected my own Java code in a running Oracle Forms application, using DLL Injection and some jni trickery. (Windows 7, 32 bits, Oracle Forms 11, JRE Java 8)

I am able to traverse the tree of Components and to query and set values in some basic Java objects, such as those from class oracle.forms.ui.VTextField

I am stuck when trying to simulate user click on a oracle.apps.fnd.ui.Button

I tried 2 things :

  1. call the simulatePush method of the AbstractButton class
  2. call the activate method of the PushButton class

(the 2 classes are in the class hierarchy for Button)

Results were identical: 1. At first, it works fine: when the button is a "Search" button, the search is done and the results are displayed. 2. Then, it immediately breaks the application, with a pop-up saying FRM-92100 Your connection to the Server was interrupted.

From there, the Application is hung.

Update: It seems that the error which cause a disconnection from the Server is:

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.access$400(Unknown Source) at java.awt.EventQueue$2.run(Unknown Source) at java.awt.EventQueue$2.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.security.AccessControlContext$1.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)

My code is here: CustomAWT.run(CustomAWT.java:34) and is called with invokeLater. Problem is probably: when calling the oracle.ewt.button.PushButton.activate method, I am NOT in the right EDT.

Using "List Threads" in the Java Console, I got:

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.

So, there is THREE AWT-EventQueue threads... Question is now: How to query/retrieve the right one, and how to make the Runnable passed to invokeLater to run in the "Good Thread" (I guess that the good one is the last one (AWT-EventQueue-2)


Solution

  • After a lot of experimentation and google searches with keywords like EventQueue and ThreadGroup I have finally found a solution (in the Works For Me category, mind you).

    I use the sun.awt.AppContext class. Some documentation and sources here (grepcode.com)

    1. Get a Collection of the running AppContext's using the getAppContexts method.
    2. For each retrieved AppContext, get his ThreadGroup using the getThreadGroup method.
    3. With the ThreadGroup object, Use the getName method.
    4. When the name of the Thread Group starts with the http: address of your Forms Application, retrieve the Object property with key name sun.awt.AppContext.EVENT_QUEUE_KEY, using the get method of AppContext.
    5. The retrieved object is an EventQueue. Create an java.awt.event.InvocationEvent object, passing your Runnable to the CTOR, and use the postEvent method of EventQueue.
    6. Your run method will be executed in the right thread.

    Remarks:

    • This answer is a specific, works for me, solution for an Oracle Forms Application launched via an Internet Explorer link, and running in a java.exe process. In that situation, the 3 Thread Groups are as shown in the question: main, Plugin Thread Group, and http://xxxx.xxxx.xxxxx.xx:8001/OA_JAVA/-threadGroup Your mileage may vary.
    • If you don't use full reflection, but instead do import sun.awt.AppContext, the compiler may emit warnings in the form warning: sun.awt.AppContext is Sun proprietary API and may be removed in a future release That's not very cool, but I will live with that, for the time being.
    • In the run method, I tested OK with the simulatePush method of oracle.ewt.lwAWT.AbstractButton.
    • The method emulated here is invokeLater. For invokeAndWait, more code is needed around the postEvent call. See some sources for the EventQueue class, as a starting point.