KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com4j > Wrapper


1 package com4j;
2
3 import java.lang.reflect.InvocationHandler JavaDoc;
4 import java.lang.reflect.InvocationTargetException JavaDoc;
5 import java.lang.reflect.Method JavaDoc;
6 import java.lang.reflect.Proxy JavaDoc;
7 import java.util.Collections JavaDoc;
8 import java.util.Map JavaDoc;
9 import java.util.WeakHashMap JavaDoc;
10
11 /**
12  *
13  *
14  * @author Kohsuke Kawaguchi (kk@kohsuke.org)
15  */

16 final class Wrapper implements InvocationHandler JavaDoc, Com4jObject {
17
18     /**
19      * interface pointer.
20      */

21     private int ptr;
22
23     /**
24      * Cached hash code. The value of IUnknown*
25      */

26     private int hashCode=0;
27
28     /**
29      * Used to form a linked list for {@link ComThread#freeList}.
30      */

31     Wrapper next;
32
33     /**
34      * All the invocation to the wrapper COM object must go through this thread.
35      */

36     private final ComThread thread;
37
38     /**
39      * Cached of {@link MethodInfo} keyed by the method decl.
40      *
41      * TODO: revisit the cache design
42      */

43     private Map JavaDoc<Method JavaDoc,MethodInfo> cache = Collections.synchronizedMap(
44         new WeakHashMap JavaDoc<Method JavaDoc,MethodInfo>());
45
46     private Wrapper(int ptr) {
47         if(ptr==0) throw new IllegalArgumentException JavaDoc();
48         assert ComThread.isComThread();
49
50         this.ptr = ptr;
51         thread = ComThread.get();
52     }
53
54     /**
55      * Creates a new proxy object to a given COM pointer.
56      * <p>
57      * Must be run from a {@link ComThread}.
58      */

59     static <T extends Com4jObject>
60     T create( Class JavaDoc<T> primaryInterface, int ptr ) {
61         Wrapper w = new Wrapper(ptr);
62         T r = primaryInterface.cast(Proxy.newProxyInstance(
63             primaryInterface.getClassLoader(),
64             new Class JavaDoc<?>[]{primaryInterface},
65                 w));
66         w.thread.addLiveObject(r);
67         return r;
68     }
69
70     /**
71      * Creates a new proxy object to a given COM pointer.
72      * <p>
73      * Must be run from a {@link ComThread}.
74      */

75     static Com4jObject create( int ptr ) {
76         Wrapper w = new Wrapper(ptr);
77         w.thread.addLiveObject(w);
78         return w;
79     }
80
81
82     int getPtr() {
83         return ptr;
84     }
85
86     protected void finalize() throws Throwable JavaDoc {
87         if(ptr!=0)
88             thread.addToFreeList(this);
89     }
90
91     private static final Object JavaDoc[] EMPTY_ARRAY = new Object JavaDoc[0];
92
93     public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
94         if(ptr==0)
95             throw new IllegalStateException JavaDoc("COM object is already disposed");
96         if(args==null) // this makes the processing easier
97
args = EMPTY_ARRAY;
98
99         Class JavaDoc<?> declClazz = method.getDeclaringClass();
100
101         if( declClazz==Com4jObject.class || declClazz==Object JavaDoc.class ) {
102             // method declared on Com4jObject is not meant to be delegated.
103
try {
104                 return method.invoke(this,args);
105             } catch( IllegalAccessException JavaDoc e ) {
106                 throw new IllegalAccessError JavaDoc(e.getMessage());
107             } catch( InvocationTargetException JavaDoc e ) {
108                 throw e.getTargetException();
109             }
110         }
111
112         if(invCache==null)
113             invCache = new InvocationThunk();
114         return invCache.invoke(getMethodInfo(method),args);
115     }
116
117     private MethodInfo getMethodInfo(Method JavaDoc method) {
118         MethodInfo r = cache.get(method);
119         if(r!=null) return r;
120         r = new MethodInfo(method);
121         cache.put(method,r);
122         return r;
123     }
124
125     public void dispose() {
126         if(ptr!=0) {
127             thread.execute(new Task<Object JavaDoc>() {
128                 public Object JavaDoc call() {
129                     dispose0();
130                     return null;
131                 }
132             });
133         }
134     }
135
136     /**
137      * Called from {@link ComThread} to actually call IUnknown::Release.
138      */

139     boolean dispose0() {
140         boolean r = ptr!=0;
141         Native.release(ptr);
142         ptr=0;
143         return r;
144     }
145
146     public <T extends Com4jObject> boolean is( Class JavaDoc<T> comInterface ) {
147         try {
148             GUID iid = COM4J.getIID(comInterface);
149             return new QITestTask(iid).execute(thread)!=0;
150         } catch( ComException e ) {
151             return false;
152         }
153     }
154
155     public <T extends Com4jObject> T queryInterface( final Class JavaDoc<T> comInterface ) {
156         return new Task<T>() {
157             public T call() {
158                 GUID iid = COM4J.getIID(comInterface);
159                 int nptr = Native.queryInterface(ptr,iid.v[0],iid.v[1]);
160                 if(nptr==0)
161                     return null; // failed to cast
162
return create( comInterface, nptr );
163             }
164         }.execute(thread);
165     }
166
167     public String JavaDoc toString() {
168         return "ComObject:"+Integer.toHexString(ptr);
169     }
170
171     public final int hashCode() {
172         if(hashCode==0) {
173             if(ptr!=0) {
174                 hashCode = new QITestTask(COM4J.IID_IUnknown).execute(thread);
175             } else {
176                 hashCode = 0;
177             }
178         }
179         return hashCode;
180     }
181
182     public final boolean equals( Object JavaDoc rhs ) {
183         if(!(rhs instanceof Com4jObject)) return false;
184         return hashCode()==rhs.hashCode();
185     }
186
187     /**
188      * Used to pass parameters/return values between the host thread
189      * and the peer {@link ComThread}.
190      */

191     private class InvocationThunk extends Task<Object JavaDoc> {
192         private MethodInfo method;
193         private Object JavaDoc[] args;
194
195         /**
196          * Invokes the method on the peer {@link ComThread} and returns
197          * its return value.
198          */

199         public synchronized Object JavaDoc invoke( MethodInfo method, Object JavaDoc[] args ) {
200             invCache = null;
201             this.method = method;
202             this.args = args;
203
204             try {
205                 return execute(thread);
206             } finally {
207                 invCache = this;
208             }
209         }
210
211         /**
212          * Called from {@link ComThread} to actually carry out the execution.
213          */

214         public synchronized Object JavaDoc call() {
215             Object JavaDoc r = method.invoke(ptr,args);
216             // clear fields that are no longer necessary
217
method = null;
218             args = null;
219             return r;
220         }
221     }
222
223     /**
224      * We cache up to one {@link InvocationThunk}.
225      */

226     InvocationThunk invCache;
227
228
229
230     /**
231      * Invokes QueryInterface but immediately releases that pointer.
232      * Useful for checking if an object implements a particular interface.
233      */

234     private final class QITestTask extends Task<Integer JavaDoc> {
235         private final GUID iid;
236
237         public QITestTask(GUID iid) {
238             this.iid = iid;
239         }
240
241         public Integer JavaDoc call() {
242             int nptr = Native.queryInterface(ptr,iid.v[0],iid.v[1]);
243             if(nptr!=0)
244                 Native.release(nptr);
245             return nptr;
246         }
247     }
248 }
249
Popular Tags