Source code: org/gui4j/core/impl/Gui4jImpl.java
1 package org.gui4j.core.impl;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.ObjectInputStream;
6 import java.io.ObjectOutputStream;
7 import java.io.Serializable;
8 import java.lang.reflect.InvocationTargetException;
9 import java.lang.reflect.UndeclaredThrowableException;
10 import java.net.URL;
11 import java.util.HashSet;
12 import java.util.Set;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.gui4j.Gui4jCallBase;
17 import org.gui4j.Gui4jController;
18 import org.gui4j.Gui4jDialog;
19 import org.gui4j.Gui4jValidator;
20 import org.gui4j.Gui4jView;
21 import org.gui4j.Gui4jWindow;
22 import org.gui4j.core.Gui4jCallFactory;
23 import org.gui4j.core.Gui4jComponentContainer;
24 import org.gui4j.core.Gui4jComponentContainerManager;
25 import org.gui4j.core.Gui4jComponentManager;
26 import org.gui4j.core.Gui4jInternal;
27 import org.gui4j.core.Gui4jReflectionManager;
28 import org.gui4j.core.Gui4jThreadManager;
29 import org.gui4j.core.call.Gui4jCallParser;
30 import org.gui4j.core.interfaces.Gui4jWindowInternal;
31 import org.gui4j.core.util.MethodCall;
32 import org.gui4j.event.SimpleEvent;
33 import org.gui4j.exception.Gui4jDefaultErrorHandler;
34 import org.gui4j.exception.Gui4jErrorHandler;
35 import org.gui4j.exception.Gui4jException;
36 import org.gui4j.exception.Gui4jExceptionHandler;
37
38
39 /**
40 * This is the base initialization class of Gui4j. An instance of
41 * Gui4j holds all other necessary instances to deal with
42 * graphical user interfaces with gui4j. Note that it is possible
43 * to use different instances of Gui4j, but each instance has its
44 * own workspace and they are completely independant; and each
45 * instance maintains its own cache for reflection calls and
46 * worker threads.
47 */
48 final class Gui4jImpl implements Serializable, Gui4jInternal
49 {
50 private Log mLogger = LogFactory.getLog(getClass());
51 private final Gui4jComponentManager mGui4jComponentManager;
52 private final Gui4jComponentContainerManager mGui4jComponentContainerManager;
53 private final Gui4jReflectionManager mGui4jReflectionManager;
54 private final Gui4jThreadManager mGui4jThreadManager;
55 private final SimpleEvent eViewCollection = new SimpleEvent();
56 private boolean mValidateXML;
57 private boolean mLogInvoke;
58 private boolean mTraceWorkerInvocation = false;
59 private Gui4jErrorHandler mErrorHandler;
60
61 private Set windowCollector = new HashSet();
62
63 public Gui4jImpl(boolean validateXML, boolean logInvoke, int numberOfWorkerThreads, URL configURL)
64 {
65 mValidateXML = validateXML;
66 mLogInvoke = logInvoke;
67 mGui4jComponentManager = Gui4jComponentManager.getNewInstance(this);
68 mGui4jComponentContainerManager = Gui4jComponentContainerManager.getNewInstance(this);
69 mGui4jReflectionManager = Gui4jReflectionManager.getNewInstance();
70 mGui4jThreadManager = Gui4jThreadManager.getNewInstance(this, numberOfWorkerThreads);
71 configure(configURL);
72 }
73
74 /**
75 * @return the contained <code>Gui4jComponentManager</code>
76 */
77 public Gui4jComponentManager getGui4jComponentManager()
78 {
79 return mGui4jComponentManager;
80 }
81
82 /**
83 * @return the contained <code>Gui4jComponentContainerManager</code>
84 */
85 public Gui4jComponentContainerManager getGui4jComponentContainerManager()
86 {
87 return mGui4jComponentContainerManager;
88 }
89
90 /**
91 * @return the contained <code>Gui4jReflectionManager</code>
92 */
93 public Gui4jReflectionManager getGui4jReflectionManager()
94 {
95 return mGui4jReflectionManager;
96 }
97
98 /**
99 * @return the used <code>Gui4jThreadManager</code>
100 */
101 public Gui4jThreadManager getGui4jThreadManager()
102 {
103 return mGui4jThreadManager;
104 }
105
106 /**
107 * @param configurationSource the URL of the XML configuration file for the
108 * <code>Gui4jComponents</code> to be installed
109 */
110 private void configure(URL configurationSource)
111 {
112 assert configurationSource != null;
113 mGui4jComponentManager.configure(configurationSource);
114 }
115
116 /**
117 * Is called after the setter of an edit field returns without raising an
118 * exception. If the given Controller (Gui4jCallBase) implements a suitable
119 * method, that method is called, otherwise, nothing happens.
120 * @param gui4jCallBase
121 * @param context
122 */
123 public void handleSuccess(Gui4jCallBase gui4jCallBase, Object context)
124 {
125 if (context == null)
126 {
127 return;
128 }
129
130 try
131 {
132 Gui4jExceptionHandler exceptionHandler =
133 gui4jCallBase != null ? gui4jCallBase.getExceptionHandler() : null;
134 Gui4jExceptionHandler oldHandler = null;
135 while (exceptionHandler != null && exceptionHandler != oldHandler)
136 {
137 oldHandler = exceptionHandler;
138 MethodCall call =
139 getGui4jReflectionManager().getMethod(
140 "errorHandling",
141 exceptionHandler.getClass(),
142 "handleSuccess",
143 new Class[] { context.getClass()},
144 false);
145 if (call != null)
146 {
147 call.invoke(exceptionHandler, new Object[] { context });
148 break;
149 }
150 exceptionHandler = exceptionHandler.getDelegationExceptionHandler();
151 }
152 }
153 catch (Throwable tHandler)
154 {
155 internalError(tHandler);
156 }
157 }
158
159 private Throwable unpack(Throwable t)
160 {
161 while (t != null)
162 {
163 if (t.getCause() != null && t instanceof UndeclaredThrowableException)
164 {
165 t = t.getCause();
166 continue;
167 }
168 if (t instanceof InvocationTargetException)
169 {
170 t = ((InvocationTargetException) t).getTargetException();
171 continue;
172 }
173 break;
174 }
175 return t;
176 }
177
178 /**
179 * Is called for all exceptions occuring during execution of methods. If the
180 * given Controller (Gui4jCallBase) defines a suitable error handler, that
181 * error handler is called. Otherwise an internal error is raisen.
182 * @param gui4jCallBase
183 * @param t
184 * @param context
185 */
186 public void handleException(Gui4jCallBase gui4jCallBase, Throwable t, Object context)
187 {
188 assert t != null;
189 t = unpack(t);
190 boolean handled = false;
191 Gui4jExceptionHandler topExceptionHandler =
192 gui4jCallBase != null ? gui4jCallBase.getExceptionHandler() : null;
193 try
194 {
195 Gui4jExceptionHandler exceptionHandler = topExceptionHandler;
196 Gui4jExceptionHandler oldHandler = null;
197 if (context != null)
198 {
199 while (!handled && exceptionHandler != null && oldHandler != exceptionHandler)
200 {
201 oldHandler = exceptionHandler;
202 MethodCall call =
203 getGui4jReflectionManager().getMethod(
204 "errorHandling",
205 exceptionHandler.getClass(),
206 "handleException",
207 new Class[] { context.getClass(), t.getClass()},
208 false);
209 if (call != null)
210 {
211 handled = true;
212 call.invoke(exceptionHandler, new Object[] { context, t });
213 }
214 exceptionHandler = exceptionHandler.getDelegationExceptionHandler();
215 }
216 }
217 exceptionHandler = topExceptionHandler;
218 oldHandler = null;
219 while (!handled && exceptionHandler != null && oldHandler != exceptionHandler)
220 {
221 oldHandler = exceptionHandler;
222 MethodCall call =
223 getGui4jReflectionManager().getMethod(
224 "errorHandling",
225 exceptionHandler.getClass(),
226 "handleException",
227 new Class[] { t.getClass()},
228 false);
229 if (call != null)
230 {
231 handled = true;
232 call.invoke(exceptionHandler, new Object[] { t });
233 }
234 }
235 }
236 catch (Throwable tExceptionHandler)
237 {
238 handled = true;
239 internalError(tExceptionHandler);
240 }
241 if (!handled)
242 {
243 internalError(t);
244 }
245 }
246
247 private void internalError(Throwable t)
248 {
249 mLogger.error("Internal error", t);
250 if (Thread.currentThread() instanceof Gui4jThreadManager.WorkerThread)
251 {
252 Gui4jThreadManager.WorkerThread wt = (Gui4jThreadManager.WorkerThread) Thread.currentThread();
253 if (wt.getCallStack() != null)
254 {
255 mLogger.error("Invoker of thread [" + wt.getName() + "] was:", wt.getCallStack());
256 }
257 }
258 try
259 {
260 if (mErrorHandler != null)
261 {
262 mErrorHandler.internalError(t);
263 }
264 else
265 {
266 Gui4jDefaultErrorHandler.getInstance().internalError(t);
267 }
268 }
269 catch (Throwable tError)
270 {
271 mLogger.error("Error occured while handling internal error", tError);
272 }
273 }
274
275 /**
276 * Loads the specified resource file in the cache. The method can be used to
277 * ensure that some XML resource files are loaded before serializing the
278 * current state.
279 * @param controllerClass the controller for the given resource file
280 * @param resourceName the name of the xml resource to load
281 */
282 public void readResourceFile(Class controllerClass, String resourceName)
283 {
284 String fullyQuantifiedName =
285 Gui4jComponentContainerManager.getResourceNameFullyQuantified(
286 Gui4jComponentContainerManager.getBaseName(controllerClass),
287 resourceName);
288
289 URL cfg = controllerClass.getResource(resourceName);
290 Gui4jComponentContainer gui4jComponentContainer =
291 mGui4jComponentContainerManager.getGui4jComponentContainer(controllerClass, fullyQuantifiedName, cfg);
292
293 if (gui4jComponentContainer.isDefined("TOP"))
294 {
295 gui4jComponentContainer.getGui4jQualifiedComponent("TOP");
296 }
297 if (gui4jComponentContainer.isDefined("MENU"))
298 {
299 gui4jComponentContainer.getGui4jQualifiedComponent("MENU");
300 }
301 }
302
303 private void writeObject(ObjectOutputStream out) throws IOException
304 {
305 mLogger = null;
306 out.defaultWriteObject();
307 }
308
309 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
310 {
311 in.defaultReadObject();
312 mLogger = LogFactory.getLog(getClass());
313 }
314
315 /**
316 * @return true if XML files should be validated
317 */
318 public boolean validateXML()
319 {
320 return mValidateXML;
321 }
322
323 /**
324 * @return true if invocation calls should be logged
325 */
326 public boolean logInvoke()
327 {
328 return mLogInvoke;
329 }
330
331 /**
332 * @return true if the call stack of invokers of a WorkerThread
333 * should be saved and then printed in case of an internal error.
334 */
335 public boolean traceWorkerInvocation()
336 {
337 return mTraceWorkerInvocation;
338 }
339
340 /**
341 * Defines if the call stack of invokers of WorkerThreads should
342 * always be saved so that it can be printed together with the
343 * thread's own stack trace in case of an internal error.<br>
344 * This feature is recommended only for debugging
345 * purposes since the negative performance impact of creating a
346 * call stack (i.e. instance of Throwable) for each invocation of a
347 * WorkerThread has not been measured.
348 * @param b true, to turn the feature on, false to turn it off
349 */
350 public void setTraceWorkerInvocation(boolean b)
351 {
352 mTraceWorkerInvocation = b;
353 }
354
355 /**
356 * @return true if special debug messages should be written. This
357 * method should be removed at some stage.
358 */
359 public boolean traceMode()
360 {
361 return false;
362 }
363
364 /**
365 * Sets the errorHandler.
366 * @param errorHandler The errorHandler to set
367 */
368 public void setErrorHandler(Gui4jErrorHandler errorHandler)
369 {
370 mErrorHandler = errorHandler;
371 }
372
373 /**
374 * Returns the viewCollector.
375 * @return Set
376 */
377 public Set getViewCollector()
378 {
379 return windowCollector;
380 }
381
382 public void addToWindowCollector(Gui4jWindowInternal window)
383 {
384 mLogger.debug("Adding window to list. Current length (before adding is): " + windowCollector.size());
385 windowCollector.add(window);
386 eViewCollection.fireEvent();
387 }
388
389 public void removeFromWindowCollector(Gui4jWindowInternal window)
390 {
391 windowCollector.remove(window);
392 mLogger.debug(
393 "Removing window from list. Current length (after removing is): " + windowCollector.size());
394 eViewCollection.fireEvent();
395 }
396
397
398 /* (non-Javadoc)
399 * @see org.gui4j.Gui4j#createView(java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
400 */
401 public Gui4jView createView(String viewResourceName, Gui4jController gui4jController, String title,
402 boolean readOnlyMode)
403 {
404 return new Gui4jViewImpl(this, viewResourceName, gui4jController, title, readOnlyMode);
405 }
406
407
408 /* (non-Javadoc)
409 * @see org.gui4j.Gui4j#createDialog(org.gui4j.Gui4jWindow, java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
410 */
411 public Gui4jDialog createDialog(Gui4jWindow owner, String viewResourceName, Gui4jController gui4jController,
412 String title, boolean readOnlyMode)
413 {
414 return new Gui4jDialogImpl(this, owner, viewResourceName, gui4jController, title, readOnlyMode);
415 }
416 public Gui4jCallFactory createCallFactory()
417 {
418 return new Gui4jCallParser();
419 }
420
421
422 /* (non-Javadoc)
423 * @see org.gui4j.Gui4j#writeDTD(java.io.File)
424 */
425 public void writeDTD(File outputFile) throws Gui4jException
426 {
427 getGui4jComponentManager().writeDTD(outputFile);
428 }
429
430
431 /* (non-Javadoc)
432 * @see org.gui4j.Gui4j#createValidator()
433 */
434 public Gui4jValidator createValidator()
435 {
436 return new Gui4jValidatorImpl(this);
437 }
438 }