-
Notifications
You must be signed in to change notification settings - Fork 121
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SF 1702604 - alpha prototype for activeXInvocationProxy event callbacks
- Loading branch information
clay_shooter
committed
Apr 18, 2007
1 parent
3a32be8
commit 4dc7dc1
Showing
13 changed files
with
747 additions
and
534 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<html> | ||
|
||
<head> | ||
<title>Jacob can register Java classes for MS application events or callbacks</title> | ||
<BODY> | ||
<H2>Events</H2> | ||
Jacob can register Java classes for MS application events or callbacks. The normal flow for this is: | ||
|
||
|
||
<OL> | ||
<LI>Application thread creates an instance of the event handler and registers it with Jacob | ||
<LI>The application continues on doing other work. | ||
<LI>Some time later, the MS application takes some action and initiates the event callback. | ||
<LI>The Java VM receives the event and spins up a new thread to handle it. | ||
<LI>The Jacob jni EventProxy in the dll is called by the VM. | ||
<LI>The Jacob jni EventProxy creates Variant objects to handle the parameters of the passed in event. | ||
<LI>The Jacob jni EventProxy sends the name of the callback and the array of Variant objects to the Java InvocationProxy that was registered to catch events. | ||
<LI>The Java InvocationProxy uses reflection to map the event name to a method name with the exact same name. | ||
<LI>The Java InvocationProxy sends the message to the registered event handler and returns if the event handler is of type void (standard behavior). | ||
<LI>The Java InvocationProxy sends the message to the registered event handler and returns the Variant that resulted from the call back to the Jacob jni EventProxy that then returns it to the windows calling program. | ||
</OL> | ||
|
||
<H2>SWING Issues</H2> | ||
Swing developers should note that this message comes in on a thread other than the event thread. | ||
All objects receiving events that require user intervention or drawing in the UI should use | ||
invokeLater() to post requests for actions onto the event queue. Failure to do so will | ||
insure random failures in the GUI. | ||
Java Web Start (JWS) and other launchers can have additional issues related to the class loader. | ||
The Jacob C++ library uses FindClass() to find the Variant class when building the parameter list. | ||
FindClass() uses the system class loader which includes only the classes specified at run time or | ||
in the CLASSPATH. Most of the application classes in this situation live in an alternate set of | ||
class loaders that were created when the launcher located and ran the application classes. This | ||
means that the search for Variant will fail usually with the silent and immediate termination of | ||
the Java application. The thread classloader probably can’t be used to try and find the class | ||
because this new thread does not have a classloader associated with it other than the system class | ||
loader. The end result is that the Variant class needs to be located via other means and that the | ||
thread classloader should be set to be the context class loader of the event handler class. | ||
|
||
<H2>1.8 and 1.9 behavior</H2> | ||
The Jacob EventProxy class has been modified (off of the 1.8 tree) so that it takes a two step approach towards fixing these problems. | ||
<OL> | ||
<LI>The EventProxy constructor now accepts an extra object, an instance of the Variant class. This gives the EventProxy a way to get to the Variant class and thus to its classloader. All of the callers of the constructor have been modified to pass a Variant object to the EventProxy without programmer intervention. | ||
<LI>EventProxy first attempts to locate the Variant class using FindClass() | ||
<LI>Failing that, it looks to see if a variant object had been passed in. If so, it calls class() and goes from there. | ||
<LI>If all that fails, it logs a message and then fails in the spectacular fashion of the previous versions. | ||
</OL> | ||
<p> | ||
<H2>1.10 behavior</H2> | ||
The Jacob EventProxy class has been modified so that it takes a different approach towards fixing this problem. | ||
<OL> | ||
<LI>All objects that request event notification are now wrapped in a Java InvocationProxy | ||
so that a standard interface is always presented to the JNI EventProxy object. | ||
<LI>The EventProxy constructor accepts any Java class. It wraps the class if it is not an | ||
InvocationProxy or uses just the passed in object if it is an InvocationProxy. | ||
The JNI layer talks to the InvocationProxy instead of talking directly to the event listener | ||
as in previous releases. | ||
<LI>The Java InvocationProxy has a method on it that will return the Variant class that the | ||
EventProxy. The JNI code uses this method to acquire the class so that it can call newInstance(). | ||
</OL> | ||
Developers can receive call back events in JWS other Java launching programs without implementing any additional code. They should be aware that their callback methods may need to set the class loader. If they expect to create any objects.: | ||
<pre> | ||
Public xxx someHandler(Variant[] foo){ | ||
Thread.currentThread().setContextClassLoader( | ||
this.getClass().getClassLoader()); | ||
// do something | ||
} | ||
</pre> | ||
There may still be a dual event queue issue in JWS applications that needs to be looked at. | ||
|
||
<p> | ||
<H2>1.12 Experimental Behavior</H2> | ||
Release 1.12 adds experimental support for event handlers that accept java objects as parameters | ||
to closer match the signature of the windows callback. New ActiveXDispatchEvents and | ||
ActiveXInvocationProxy operate in tandem in the same way as DispatchEvents and InvocationProxy. | ||
DispatchEvents overrides getInvocationProxy() to create a new ActiveXInvocationProxy in place | ||
of the normal InvocationProxy. ActiveXInvocationProxy has its own invoke() method that uses | ||
reflection to call back using java objects as parameters. | ||
<p> | ||
Issues with this approach | ||
<ul> | ||
<li>Event callbacks that use java signatures do not support parameter modification. Many | ||
windows callbacks let a user reject an event that is about to happen by modifying one of the | ||
parameters. In this situation, the old DispatchEvents/InvocationProxy pair must be used instead | ||
of the new handlers. | ||
</ul> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright (c) 1999-2004 Sourceforge JACOB Project. | ||
* All rights reserved. Originator: Dan Adler (http://danadler.com). | ||
* Get more information about JACOB at http://sourceforge.net/projects/jacob-project | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
package com.jacob.activeX; | ||
|
||
import com.jacob.com.Dispatch; | ||
import com.jacob.com.DispatchEvents; | ||
import com.jacob.com.InvocationProxy; | ||
|
||
/** | ||
* RELEASE 1.12 EXPERIMENTAL. | ||
* <p> | ||
* Use this exactly like the DispatchEvents class. This class plugs in | ||
* an ActiveXInvocationProxy instead of an InvocationProxy. It is the | ||
* ActiveXInvocationProxy that implements the reflection calls and invoke | ||
* the found java event callbacks. See ActiveXInvocationProxy for details. | ||
* | ||
* | ||
*/ | ||
public class ActiveXDispatchEvents extends DispatchEvents { | ||
|
||
/** | ||
* This is the most commonly used constructor. | ||
* <p> | ||
* Creates the event callback linkage between the the | ||
* MS program represented by the Dispatch object and the | ||
* Java object that will receive the callback. | ||
* @param sourceOfEvent Dispatch object who's MS app will generate callbacks | ||
* @param eventSink Java object that wants to receive the events | ||
*/ | ||
public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink) { | ||
super(sourceOfEvent, eventSink, null ); | ||
} | ||
|
||
/** | ||
* None of the samples use this constructor. | ||
* <p> | ||
* Creates the event callback linkage between the the | ||
* MS program represented by the Dispatch object and the | ||
* Java object that will receive the callback. | ||
* @param sourceOfEvent Dispatch object who's MS app will generate callbacks | ||
* @param eventSink Java object that wants to receive the events | ||
* @param progId ??? | ||
*/ | ||
public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId) { | ||
super(sourceOfEvent, eventSink, progId, null ); | ||
} | ||
|
||
/** | ||
* Creates the event callback linkage between the the | ||
* MS program represented by the Dispatch object and the | ||
* Java object that will receive the callback. | ||
* <pre> | ||
* >ActiveXDispatchEvents de = | ||
* new ActiveXDispatchEvents(someDispatch,someEventHAndler, | ||
* "Excel.Application", | ||
* "C:\\Program Files\\Microsoft Office\\OFFICE11\\EXCEL.EXE"); | ||
* | ||
* @param sourceOfEvent Dispatch object who's MS app will generate callbacks | ||
* @param eventSink Java object that wants to receive the events | ||
* @param progId , mandatory if the typelib is specified | ||
* @param typeLib The location of the typelib to use | ||
*/ | ||
public ActiveXDispatchEvents(Dispatch sourceOfEvent, Object eventSink, String progId, String typeLib) | ||
{ | ||
super(sourceOfEvent, eventSink, progId, typeLib); | ||
} | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see com.jacob.com.DispatchEvents#getInvocationProxy(java.lang.Object) | ||
*/ | ||
protected InvocationProxy getInvocationProxy(Object pTargetObject){ | ||
InvocationProxy newProxy = new ActiveXInvocationProxy(); | ||
newProxy.setTarget(pTargetObject); | ||
return newProxy; | ||
} | ||
|
||
} |
167 changes: 167 additions & 0 deletions
167
jacob/src/com/jacob/activeX/ActiveXInvocationProxy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* | ||
* Copyright (c) 1999-2004 Sourceforge JACOB Project. | ||
* All rights reserved. Originator: Dan Adler (http://danadler.com). | ||
* Get more information about JACOB at http://sourceforge.net/projects/jacob-project | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* This library is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
package com.jacob.activeX; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
|
||
import com.jacob.com.InvocationProxy; | ||
import com.jacob.com.NotImplementedException; | ||
import com.jacob.com.Variant; | ||
|
||
/** | ||
* RELEASE 1.12 EXPERIMENTAL. | ||
* <p> | ||
* This class that lets event handlers receive events with all java | ||
* objects as parameters. The standard Jacob event methods all accept an array of | ||
* Variant objects. When using this class, you can set up your event methods | ||
* as regular java methods with the correct number of parameters of the correct | ||
* java type. This does NOT work for any event that wishes to accept a call | ||
* back and modify the calling parameters to tell windows what to do. An example | ||
* is when an event lets the receiver cancel the action by setting a boolean flag | ||
* to false. The java objects cannot be modified and their values will not be passed | ||
* back into the originating Variants even if they could be modified. | ||
* <p> | ||
* This class acts as a proxy between the windows event callback mechanism and | ||
* the Java classes that are looking for events. It assumes that | ||
* all of the Java classes that are looking for events implement methods with the | ||
* same names as the windows events and that the implemented methods native | ||
* java objects of the type and order that match the windows documentation. | ||
* The methods can return void or a Variant that | ||
* will be returned to the calling layer. All Event methods that will be | ||
* recognized by InvocationProxyAllEvents have the signature | ||
* | ||
* <code> void eventMethodName(Object,Object...)</code> | ||
* or | ||
* <code> Object eventMethodName(Object,Object...)</code> | ||
*/ | ||
public class ActiveXInvocationProxy extends InvocationProxy { | ||
|
||
/* | ||
* (non-Javadoc) | ||
* @see com.jacob.com.InvocationProxy#invoke(java.lang.String, com.jacob.com.Variant[]) | ||
*/ | ||
public Variant invoke(String methodName, Variant targetParameters[]) { | ||
Variant mVariantToBeReturned = null; | ||
if (mTargetObject == null){ | ||
// structured programming guidlines say this return should not be up here | ||
return null; | ||
} | ||
Class targetClass = mTargetObject.getClass(); | ||
if (methodName == null){ | ||
throw new IllegalArgumentException("InvocationProxy: missing method name"); | ||
} | ||
if (targetParameters == null){ | ||
throw new IllegalArgumentException("InvocationProxy: missing Variant parameters"); | ||
} | ||
try { | ||
Method targetMethod; | ||
Object parametersAsJavaObjects[] = getParametersAsJavaObjects(targetParameters); | ||
Class parametersAsJavaClasses[] = getParametersAsJavaClasses(parametersAsJavaObjects); | ||
targetMethod = targetClass.getMethod(methodName, parametersAsJavaClasses); | ||
if (targetMethod != null){ | ||
// protected classes can't be invoked against even if they | ||
// let you grab the method. you could do targetMethod.setAccessible(true); | ||
// but that should be stopped by the security manager | ||
Object mReturnedByInvocation = null; | ||
mReturnedByInvocation = | ||
targetMethod.invoke(mTargetObject,parametersAsJavaObjects); | ||
if (mReturnedByInvocation == null){ | ||
mVariantToBeReturned = null; | ||
} else if (!(mReturnedByInvocation instanceof Variant)){ | ||
mVariantToBeReturned = new Variant(mReturnedByInvocation); | ||
} else { | ||
mVariantToBeReturned = (Variant) mReturnedByInvocation; | ||
} | ||
} | ||
} catch (SecurityException e) { | ||
// what causes this exception? | ||
e.printStackTrace(); | ||
} catch (NoSuchMethodException e) { | ||
// this happens whenever the listener doesn't implement all the methods | ||
} catch (IllegalArgumentException e) { | ||
// we can throw these inside the catch block so need to re-throw it | ||
Exception oneWeShouldToss = new IllegalArgumentException("Unable to map parameters for method "+methodName | ||
+ ": "+e.toString()); | ||
oneWeShouldToss.printStackTrace(); | ||
} catch (IllegalAccessException e) { | ||
// can't access the method on the target instance for some reason | ||
e.printStackTrace(); | ||
} catch (InvocationTargetException e) { | ||
// invocation of target method failed | ||
e.printStackTrace(); | ||
} | ||
return mVariantToBeReturned; | ||
|
||
} | ||
|
||
/** | ||
* creates a method signature compatible array of classes from an array of parameters | ||
* @param parametersAsJavaObjects | ||
* @return | ||
*/ | ||
private Class[] getParametersAsJavaClasses(Object[] parametersAsJavaObjects) { | ||
if (parametersAsJavaObjects == null){ | ||
throw new IllegalArgumentException("This only works with an array of parameters"); | ||
} | ||
int numParameters = parametersAsJavaObjects.length; | ||
Class parametersAsJavaClasses[] = new Class[numParameters]; | ||
for ( int parameterIndex = 0 ; parameterIndex < numParameters; parameterIndex++){ | ||
Object oneParameterObject = parametersAsJavaObjects[parameterIndex]; | ||
if (oneParameterObject == null){ | ||
parametersAsJavaClasses[parameterIndex] = null; | ||
} else { | ||
Class oneParameterClass = oneParameterObject.getClass(); | ||
parametersAsJavaClasses[parameterIndex] = oneParameterClass; | ||
} | ||
} | ||
return parametersAsJavaClasses; | ||
} | ||
|
||
/** | ||
* converts an array of Variants to their associated Java types | ||
* @param targetParameters | ||
* @return | ||
*/ | ||
private Object[] getParametersAsJavaObjects(Variant[] targetParameters) { | ||
if (targetParameters == null){ | ||
throw new IllegalArgumentException("This only works with an array of parameters"); | ||
} | ||
int numParameters = targetParameters.length; | ||
Object parametersAsJavaObjects[] = new Object[numParameters]; | ||
for ( int parameterIndex = 0 ; parameterIndex < numParameters; parameterIndex++){ | ||
Variant oneParameterObject = targetParameters[parameterIndex]; | ||
if (oneParameterObject == null){ | ||
parametersAsJavaObjects[parameterIndex] = null; | ||
} else { | ||
try { | ||
parametersAsJavaObjects[parameterIndex] = oneParameterObject.toJavaObject(); | ||
} catch (NotImplementedException nie){ | ||
throw new IllegalArgumentException("Can't convert parameter "+parameterIndex+ | ||
" type "+oneParameterObject.getvt() | ||
+" to java object: "+nie.getMessage()); | ||
} | ||
} | ||
} | ||
return parametersAsJavaObjects; | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.