1 | /* |
2 | * $Id: OleThread.java,v 1.5 2005/12/22 19:37:15 hastings Exp $ |
3 | * |
4 | * (c) Copyright, Moebius Solutions, Inc., 2004 |
5 | * |
6 | * All Rights Reserved |
7 | * |
8 | * This material may be reproduced by or for the U. S. Government |
9 | * pursuant to the copyright license under the clause at |
10 | * DFARS 252.227-7014 (OCT 2001). |
11 | */ |
12 | |
13 | package com.moesol.bindings.platform_sdk.component_services; |
14 | |
15 | import java.lang.reflect.InvocationTargetException; |
16 | |
17 | import com.moesol.bindings.NativeLibraryLoader; |
18 | import com.moesol.bindings.platform_sdk.windows_api.MSG; |
19 | import com.moesol.bindings.platform_sdk.windows_api.PlatformSDK; |
20 | |
21 | /** |
22 | * Creates a COM/OLE Apartment Thread. In the COM/OLE architecture |
23 | * Aparment threads must pump windows messages. OleThread satisfies this |
24 | * requirement. Typical usage is to create a subclass that is a singleton. |
25 | * <pre> |
26 | * public class MyOleThread extends OleThread { |
27 | * private MyOleThread() { |
28 | * super("MyOleThread"); |
29 | * } |
30 | * public static synchronized MyOleThread instance() { |
31 | * if (s_instance == null) { |
32 | * s_instance = new MyOleThread(); |
33 | * s_instance.startAndInit(); |
34 | * } |
35 | * return s_instance; |
36 | * } |
37 | * |
38 | * private static MyOleThread s_instance = null; |
39 | * } |
40 | * </pre> |
41 | * |
42 | * You can use invokeLater and invokeAndWait in a similar manner as the |
43 | * EventQueue. |
44 | * <pre> |
45 | * MyOleThread.instance().invokeLater(new Runnable() { |
46 | * public void run() { |
47 | * // code that runs on MyOleThread and in the |
48 | * // MyOleThread apartment. |
49 | * } |
50 | * }); |
51 | * </pre> |
52 | * |
53 | * @version $Revision: 1.5 $ $Date: 2005/12/22 19:37:15 $ |
54 | */ |
55 | public class OleThread implements Runnable { |
56 | static { |
57 | NativeLibraryLoader.loadLibrary("com_moesol_bindings"); |
58 | } |
59 | |
60 | public OleThread(String name) { |
61 | m_java_thread_name = name; |
62 | com_ptr = JNIOleThread(); |
63 | } |
64 | public void finalize() { |
65 | release(); |
66 | } |
67 | public Thread getJavaThread() { |
68 | return m_java_thread; |
69 | } |
70 | public static OleThread getCurrentOleThread() { |
71 | return (OleThread)s_current_ole_thread.get(); |
72 | } |
73 | public static void oleInitialize() { |
74 | JNIoleInitialize(); |
75 | } |
76 | public static void oleUninitialize() { |
77 | JNIoleUninitialize(); |
78 | } |
79 | public synchronized void startAndInit() { |
80 | m_java_thread = new Thread(this, m_java_thread_name); |
81 | m_java_thread.setDaemon(true); |
82 | m_java_thread.start(); |
83 | try { |
84 | wait(); |
85 | } catch (InterruptedException e) { |
86 | throw new RuntimeException("failed to init ole thread"); |
87 | } |
88 | } |
89 | public synchronized void attachCurrent() { |
90 | s_current_ole_thread.set(this); |
91 | |
92 | m_java_thread = Thread.currentThread(); |
93 | m_java_thread_name = m_java_thread.getName(); |
94 | JNIcreateWindow(com_ptr); |
95 | } |
96 | public synchronized void stopAndDeinit() { |
97 | m_stopping = true; |
98 | JNIpostQuit(com_ptr); |
99 | } |
100 | public void run() { |
101 | s_current_ole_thread.set(this); |
102 | |
103 | JNIoleInitialize(); |
104 | JNIcreateWindow(com_ptr); |
105 | synchronized (this) { |
106 | notify(); |
107 | } |
108 | MSG msg = new MSG(); |
109 | while (PlatformSDK.GetMessage(msg, null, 0, 0)) { |
110 | PlatformSDK.DispatchMessage(msg); |
111 | } |
112 | |
113 | JNIoleUninitialize(); |
114 | } |
115 | public void invokeLater(Runnable runable) { |
116 | conditionalThrowNotStartedExcpt(); |
117 | if (m_stopping) { |
118 | throw new IllegalStateException("OleThread " + this + " stopping."); |
119 | } |
120 | java.awt.event.InvocationEvent inv_evt |
121 | = new java.awt.event.InvocationEvent(this, runable); |
122 | JNIpostEvent(com_ptr, inv_evt); |
123 | } |
124 | public void invokeAndWait(Runnable runable) |
125 | throws InterruptedException, java.lang.reflect.InvocationTargetException |
126 | { |
127 | conditionalThrowNotStartedExcpt(); |
128 | if (Thread.currentThread() == getJavaThread()) { |
129 | throw new IllegalStateException("already on the ole thread"); |
130 | } |
131 | if (m_stopping) { |
132 | throw new IllegalStateException("OleThread " + this + " stopping."); |
133 | } |
134 | |
135 | Object signal = new Object(); |
136 | java.awt.event.InvocationEvent inv_evt |
137 | = new java.awt.event.InvocationEvent(this, runable, signal, true); |
138 | |
139 | // argv, you really cannot assert anything about |
140 | // what the GC will do. These assert's fail sometimes |
141 | // because the GC has not released these refs yet. |
142 | // AssertNoReference ref_inv_evt = null; |
143 | // assert(null != (ref_inv_evt = new AssertNoReference(inv_evt))); |
144 | |
145 | synchronized (signal) { |
146 | JNIpostEvent(com_ptr, inv_evt); |
147 | signal.wait(); |
148 | } |
149 | |
150 | if (inv_evt.getException() != null) { |
151 | throw new java.lang.reflect.InvocationTargetException(inv_evt.getException()); |
152 | } |
153 | |
154 | inv_evt = null; |
155 | // assert(!ref_inv_evt.isReferenced()); |
156 | } |
157 | |
158 | /** |
159 | * Call invokeAndWait and wrap exceptions in a RuntimeException. |
160 | * This method makes calling invokeAndWait easy by mapping checked expections |
161 | * to runtime exceptions. |
162 | * |
163 | * @param runnable |
164 | */ |
165 | public void invokeAndWaitOrRuntimeException(Runnable runnable) { |
166 | try { |
167 | invokeAndWait(runnable); |
168 | } catch (InterruptedException e ) { |
169 | throw new RuntimeException("invokeAndWait interrupted", e); |
170 | } catch (InvocationTargetException e) { |
171 | // TODO consider unwrapping the target exceptions cause here... |
172 | throw new RuntimeException("invokeAndWait caused InvocationTargetException", e); |
173 | } |
174 | } |
175 | |
176 | /** |
177 | * Call invokeAndWaitOrRuntimeException unless we are already running |
178 | * on the thread associated with this OleThread. |
179 | * |
180 | * @param runnable |
181 | * @deprecated Use safeInvokeAndWait instead. |
182 | */ |
183 | public void simpleInvokeAndWait(Runnable runnable) { |
184 | conditionalThrowNotStartedExcpt(); |
185 | if (Thread.currentThread() == getJavaThread()) { |
186 | runnable.run(); |
187 | } else { |
188 | invokeAndWaitOrRuntimeException(runnable); |
189 | } |
190 | } |
191 | |
192 | /** |
193 | * Call simpleInvokeAndWait, but wrap any Error thrown in a RuntimeException |
194 | * so that the error will propagate back to the caller. |
195 | */ |
196 | public void safeInvokeAndWait(Runnable runnable) { |
197 | class SafeRunner implements Runnable { |
198 | public SafeRunner(Runnable real_runnable) { |
199 | m_real_runnable = real_runnable; |
200 | } |
201 | public void run() { |
202 | try { |
203 | m_real_runnable.run(); |
204 | } catch (Error e) { |
205 | m_error = e; |
206 | } |
207 | } |
208 | public Error getError() { |
209 | return m_error; |
210 | } |
211 | |
212 | private Runnable m_real_runnable = null; |
213 | private Error m_error = null; |
214 | } |
215 | final SafeRunner sr = new SafeRunner(runnable); |
216 | simpleInvokeAndWait(sr); |
217 | if (sr.getError() != null) { |
218 | throw new RuntimeException(sr.getError()); |
219 | } |
220 | } |
221 | |
222 | private void release() { |
223 | if (com_ptr != 0) { |
224 | JNIrelease(com_ptr); |
225 | com_ptr = 0; |
226 | } |
227 | } |
228 | |
229 | private void conditionalThrowNotStartedExcpt() { |
230 | if (m_java_thread == null) { |
231 | throw new IllegalStateException("OleThread " + this + " has not been started or attached."); |
232 | } |
233 | } |
234 | |
235 | private static native void JNIoleInitialize(); |
236 | private static native void JNIoleUninitialize(); |
237 | private static native int JNIOleThread(); |
238 | private static native void JNIrelease(int com_ptr); |
239 | private static native void JNIcreateWindow(int com_ptr); |
240 | private static native void JNIpostEvent(int com_ptr, java.awt.event.InvocationEvent inv_evt); |
241 | private static native void JNIpostQuit(int com_ptr); |
242 | |
243 | private static ThreadLocal s_current_ole_thread = new ThreadLocal(); |
244 | /** |
245 | * Java thread we are attached to |
246 | */ |
247 | private Thread m_java_thread; |
248 | private String m_java_thread_name; |
249 | /** |
250 | * COM pointer for peer COM object. |
251 | */ |
252 | private int com_ptr = 0; |
253 | /** Set once we are stopping */ |
254 | private boolean m_stopping = false; |
255 | } |