001: package org.gui4j.core.impl;
002:
003: import java.awt.Dialog;
004: import java.awt.Frame;
005: import java.io.File;
006: import java.io.IOException;
007: import java.io.ObjectInputStream;
008: import java.io.ObjectOutputStream;
009: import java.io.Serializable;
010: import java.lang.reflect.InvocationTargetException;
011: import java.lang.reflect.UndeclaredThrowableException;
012: import java.net.URL;
013: import java.util.HashSet;
014: import java.util.Set;
015:
016: import org.apache.commons.logging.Log;
017: import org.apache.commons.logging.LogFactory;
018:
019: import org.gui4j.Gui4jCallBase;
020: import org.gui4j.Gui4jController;
021: import org.gui4j.Gui4jDialog;
022: import org.gui4j.Gui4jResourceProvider;
023: import org.gui4j.Gui4jValidator;
024: import org.gui4j.Gui4jView;
025: import org.gui4j.Gui4jWindow;
026: import org.gui4j.core.Gui4jCallFactory;
027: import org.gui4j.core.Gui4jComponentContainer;
028: import org.gui4j.core.Gui4jComponentContainerManager;
029: import org.gui4j.core.Gui4jComponentManager;
030: import org.gui4j.core.Gui4jInternal;
031: import org.gui4j.core.Gui4jReflectionManager;
032: import org.gui4j.core.Gui4jThreadManager;
033: import org.gui4j.core.call.Gui4jCallParser;
034: import org.gui4j.core.interfaces.Gui4jWindowInternal;
035: import org.gui4j.core.util.MethodCall;
036: import org.gui4j.event.SimpleEvent;
037: import org.gui4j.exception.Gui4jDefaultErrorHandler;
038: import org.gui4j.exception.Gui4jErrorHandler;
039: import org.gui4j.exception.Gui4jException;
040: import org.gui4j.exception.Gui4jExceptionHandler;
041:
042: /**
043: * This is the base initialization class of Gui4j. An instance of
044: * Gui4j holds all other necessary instances to deal with
045: * graphical user interfaces with gui4j. Note that it is possible
046: * to use different instances of Gui4j, but each instance has its
047: * own workspace and they are completely independant; and each
048: * instance maintains its own cache for reflection calls and
049: * worker threads.
050: */
051: final class Gui4jImpl implements Serializable, Gui4jInternal {
052: private Log mLogger = LogFactory.getLog(getClass());
053: private final Gui4jComponentManager mGui4jComponentManager;
054: private final Gui4jComponentContainerManager mGui4jComponentContainerManager;
055: private final Gui4jReflectionManager mGui4jReflectionManager;
056: private final Gui4jThreadManager mGui4jThreadManager;
057: private final SimpleEvent eViewCollection = new SimpleEvent();
058: private boolean mValidateXML;
059: private boolean mLogInvoke;
060: private boolean mTraceWorkerInvocation = false;
061: private Gui4jErrorHandler mErrorHandler;
062: private Gui4jResourceProvider mResourceProvider;
063:
064: private Set windowCollector = new HashSet();
065:
066: public Gui4jImpl(boolean validateXML, boolean logInvoke,
067: int numberOfWorkerThreads, URL configURL) {
068: mValidateXML = validateXML;
069: mLogInvoke = logInvoke;
070: mGui4jComponentManager = Gui4jComponentManager
071: .getNewInstance(this );
072: mGui4jComponentContainerManager = Gui4jComponentContainerManager
073: .getNewInstance(this );
074: mGui4jReflectionManager = Gui4jReflectionManager
075: .getNewInstance();
076: mGui4jThreadManager = Gui4jThreadManager.getNewInstance(this ,
077: numberOfWorkerThreads);
078: mResourceProvider = new Gui4jResourceProviderImpl();
079: configure(configURL);
080: }
081:
082: /**
083: * @return the contained <code>Gui4jComponentManager</code>
084: */
085: public Gui4jComponentManager getGui4jComponentManager() {
086: return mGui4jComponentManager;
087: }
088:
089: /**
090: * @return the contained <code>Gui4jComponentContainerManager</code>
091: */
092: public Gui4jComponentContainerManager getGui4jComponentContainerManager() {
093: return mGui4jComponentContainerManager;
094: }
095:
096: /**
097: * @return the contained <code>Gui4jReflectionManager</code>
098: */
099: public Gui4jReflectionManager getGui4jReflectionManager() {
100: return mGui4jReflectionManager;
101: }
102:
103: /**
104: * @return the used <code>Gui4jThreadManager</code>
105: */
106: public Gui4jThreadManager getGui4jThreadManager() {
107: return mGui4jThreadManager;
108: }
109:
110: /**
111: * @param configurationSource the URL of the XML configuration file for the
112: * <code>Gui4jComponents</code> to be installed
113: */
114: private void configure(URL configurationSource) {
115: assert configurationSource != null;
116: mGui4jComponentManager.configure(configurationSource);
117: }
118:
119: /**
120: * Is called after the setter of an edit field returns without raising an
121: * exception. If the given Controller (Gui4jCallBase) implements a suitable
122: * method, that method is called, otherwise, nothing happens.
123: * @param gui4jCallBase
124: * @param context
125: */
126: public void handleSuccess(Gui4jCallBase gui4jCallBase,
127: Object context) {
128: if (context == null) {
129: return;
130: }
131:
132: try {
133: Gui4jExceptionHandler exceptionHandler = gui4jCallBase != null ? gui4jCallBase
134: .getExceptionHandler()
135: : null;
136: Gui4jExceptionHandler oldHandler = null;
137: while (exceptionHandler != null
138: && exceptionHandler != oldHandler) {
139: oldHandler = exceptionHandler;
140: MethodCall call = getGui4jReflectionManager()
141: .getMethod("errorHandling",
142: exceptionHandler.getClass(),
143: "handleSuccess",
144: new Class[] { context.getClass() },
145: false);
146: if (call != null) {
147: call.invoke(exceptionHandler,
148: new Object[] { context });
149: break;
150: }
151: exceptionHandler = exceptionHandler
152: .getDelegationExceptionHandler();
153: }
154: } catch (Throwable tHandler) {
155: internalError(tHandler);
156: }
157: }
158:
159: private Throwable unpack(Throwable t) {
160: while (t != null) {
161: if (t.getCause() != null
162: && t instanceof UndeclaredThrowableException) {
163: t = t.getCause();
164: continue;
165: }
166: if (t instanceof InvocationTargetException) {
167: t = ((InvocationTargetException) t)
168: .getTargetException();
169: continue;
170: }
171: break;
172: }
173: return t;
174: }
175:
176: /**
177: * Is called for all exceptions occuring during execution of methods. If the
178: * given Controller (Gui4jCallBase) defines a suitable error handler, that
179: * error handler is called. Otherwise an internal error is raisen.
180: * @param gui4jCallBase
181: * @param t
182: * @param context
183: */
184: public void handleException(Gui4jCallBase gui4jCallBase,
185: Throwable t, Object context) {
186: assert t != null;
187: t = unpack(t);
188: boolean handled = false;
189: Gui4jExceptionHandler topExceptionHandler = gui4jCallBase != null ? gui4jCallBase
190: .getExceptionHandler()
191: : null;
192: try {
193: Gui4jExceptionHandler exceptionHandler = topExceptionHandler;
194: Gui4jExceptionHandler oldHandler = null;
195: if (context != null) {
196: while (!handled && exceptionHandler != null
197: && oldHandler != exceptionHandler) {
198: oldHandler = exceptionHandler;
199: MethodCall call = getGui4jReflectionManager()
200: .getMethod(
201: "errorHandling",
202: exceptionHandler.getClass(),
203: "handleException",
204: new Class[] { context.getClass(),
205: t.getClass() }, false);
206: if (call != null) {
207: handled = true;
208: call.invoke(exceptionHandler, new Object[] {
209: context, t });
210: }
211: exceptionHandler = exceptionHandler
212: .getDelegationExceptionHandler();
213: }
214: }
215: exceptionHandler = topExceptionHandler;
216: oldHandler = null;
217: while (!handled && exceptionHandler != null
218: && oldHandler != exceptionHandler) {
219: oldHandler = exceptionHandler;
220: MethodCall call = getGui4jReflectionManager()
221: .getMethod("errorHandling",
222: exceptionHandler.getClass(),
223: "handleException",
224: new Class[] { t.getClass() }, false);
225: if (call != null) {
226: handled = true;
227: call.invoke(exceptionHandler, new Object[] { t });
228: }
229: }
230: } catch (Throwable tExceptionHandler) {
231: handled = true;
232: internalError(tExceptionHandler);
233: }
234: if (!handled) {
235: internalError(t);
236: }
237: }
238:
239: private void internalError(Throwable t) {
240: mLogger.error("Internal error", t);
241: if (Thread.currentThread() instanceof Gui4jThreadManager.WorkerThread) {
242: Gui4jThreadManager.WorkerThread wt = (Gui4jThreadManager.WorkerThread) Thread
243: .currentThread();
244: if (wt.getCallStack() != null) {
245: mLogger.error("Invoker of thread [" + wt.getName()
246: + "] was:", wt.getCallStack());
247: }
248: }
249: try {
250: if (mErrorHandler != null) {
251: mErrorHandler.internalError(t);
252: } else {
253: Gui4jDefaultErrorHandler.getInstance().internalError(t);
254: }
255: } catch (Throwable tError) {
256: mLogger.error(
257: "Error occured while handling internal error",
258: tError);
259: }
260: }
261:
262: /**
263: * Loads the specified resource file in the cache. The method can be used to
264: * ensure that some XML resource files are loaded before serializing the
265: * current state.
266: * @param controllerClass the controller for the given resource file
267: * @param resourceName the name of the xml resource to load
268: */
269: public void readResourceFile(Class controllerClass,
270: String resourceName) {
271: String fullyQuantifiedName = Gui4jComponentContainerManager
272: .getResourceNameFullyQuantified(
273: Gui4jComponentContainerManager
274: .getBaseName(controllerClass),
275: resourceName);
276:
277: Gui4jComponentContainer gui4jComponentContainer = mGui4jComponentContainerManager
278: .getGui4jComponentContainer(controllerClass,
279: fullyQuantifiedName);
280:
281: if (gui4jComponentContainer.isDefined("TOP")) {
282: gui4jComponentContainer.getGui4jQualifiedComponent("TOP");
283: }
284: if (gui4jComponentContainer.isDefined("MENU")) {
285: gui4jComponentContainer.getGui4jQualifiedComponent("MENU");
286: }
287: }
288:
289: private void writeObject(ObjectOutputStream out) throws IOException {
290: mLogger = null;
291: out.defaultWriteObject();
292: }
293:
294: private void readObject(ObjectInputStream in) throws IOException,
295: ClassNotFoundException {
296: in.defaultReadObject();
297: mLogger = LogFactory.getLog(getClass());
298: }
299:
300: /**
301: * @return true if XML files should be validated
302: */
303: public boolean validateXML() {
304: return mValidateXML;
305: }
306:
307: /**
308: * @return true if invocation calls should be logged
309: */
310: public boolean logInvoke() {
311: return mLogInvoke;
312: }
313:
314: /**
315: * @return true if the call stack of invokers of a WorkerThread
316: * should be saved and then printed in case of an internal error.
317: */
318: public boolean traceWorkerInvocation() {
319: return mTraceWorkerInvocation;
320: }
321:
322: /**
323: * Defines if the call stack of invokers of WorkerThreads should
324: * always be saved so that it can be printed together with the
325: * thread's own stack trace in case of an internal error.<br>
326: * This feature is recommended only for debugging
327: * purposes since the negative performance impact of creating a
328: * call stack (i.e. instance of Throwable) for each invocation of a
329: * WorkerThread has not been measured.
330: * @param b true, to turn the feature on, false to turn it off
331: */
332: public void setTraceWorkerInvocation(boolean b) {
333: mTraceWorkerInvocation = b;
334: }
335:
336: /**
337: * @return true if special debug messages should be written. This
338: * method should be removed at some stage.
339: */
340: public boolean traceMode() {
341: return false;
342: }
343:
344: /**
345: * Sets the errorHandler.
346: * @param errorHandler The errorHandler to set
347: */
348: public void setErrorHandler(Gui4jErrorHandler errorHandler) {
349: mErrorHandler = errorHandler;
350: }
351:
352: /**
353: * Returns the viewCollector.
354: * @return Set
355: */
356: public Set getViewCollector() {
357: return windowCollector;
358: }
359:
360: public void addToWindowCollector(Gui4jWindowInternal window) {
361: mLogger
362: .debug("Adding window to list. Current length (before adding is): "
363: + windowCollector.size());
364: windowCollector.add(window);
365: eViewCollection.fireEvent();
366: }
367:
368: public void removeFromWindowCollector(Gui4jWindowInternal window) {
369: windowCollector.remove(window);
370: mLogger
371: .debug("Removing window from list. Current length (after removing is): "
372: + windowCollector.size());
373: eViewCollection.fireEvent();
374: }
375:
376: /* (non-Javadoc)
377: * @see org.gui4j.Gui4j#createView(java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
378: */
379: public Gui4jView createView(String viewResourceName,
380: Gui4jController gui4jController, String title,
381: boolean readOnlyMode) {
382: return new Gui4jViewImpl(this , viewResourceName,
383: gui4jController, title, readOnlyMode);
384: }
385:
386: /* (non-Javadoc)
387: * @see org.gui4j.Gui4j#createDialog(org.gui4j.Gui4jWindow, java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
388: */
389: public Gui4jDialog createDialog(Gui4jWindow owner,
390: String viewResourceName, Gui4jController gui4jController,
391: String title, boolean readOnlyMode) {
392: return new Gui4jDialogImpl(this , owner, viewResourceName,
393: gui4jController, title, readOnlyMode);
394: }
395:
396: /* (non-Javadoc)
397: * @see org.gui4j.Gui4j#createDialog(java.awt.Dialog, java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
398: */
399: public Gui4jDialog createDialog(Dialog owner,
400: String viewResourceName, Gui4jController gui4jController,
401: String title, boolean readOnlyMode) {
402: return new Gui4jDialogImpl(this , owner, viewResourceName,
403: gui4jController, title, readOnlyMode);
404: }
405:
406: /* (non-Javadoc)
407: * @see org.gui4j.Gui4j#createDialog(java.awt.Frame, java.lang.String, org.gui4j.Gui4jController, java.lang.String, boolean)
408: */
409: public Gui4jDialog createDialog(Frame owner,
410: String viewResourceName, Gui4jController gui4jController,
411: String title, boolean readOnlyMode) {
412: return new Gui4jDialogImpl(this , owner, viewResourceName,
413: gui4jController, title, readOnlyMode);
414: }
415:
416: public Gui4jCallFactory createCallFactory() {
417: return new Gui4jCallParser();
418: }
419:
420: /* (non-Javadoc)
421: * @see org.gui4j.Gui4j#writeDTD(java.io.File)
422: */
423: public void writeDTD(File outputFile) throws Gui4jException {
424: getGui4jComponentManager().writeDTD(outputFile);
425: }
426:
427: public void shutdown() {
428: getGui4jThreadManager().shutdown();
429: getGui4jComponentContainerManager().clearResources();
430: getGui4jComponentManager().dispose();
431: getGui4jReflectionManager().dispose();
432: }
433:
434: /* (non-Javadoc)
435: * @see org.gui4j.Gui4j#createValidator()
436: */
437: public Gui4jValidator createValidator() {
438: return new Gui4jValidatorImpl(this );
439: }
440:
441: /* (non-Javadoc)
442: * @see org.gui4j.Gui4j#setResourceProvider(org.gui4j.Gui4jResourceProvider)
443: */
444: public void setResourceProvider(
445: Gui4jResourceProvider resourceProvider) {
446: mResourceProvider = resourceProvider;
447: }
448:
449: /* (non-Javadoc)
450: * @see org.gui4j.core.Gui4jInternal#getResourceProvider()
451: */
452: public Gui4jResourceProvider getResourceProvider() {
453: return mResourceProvider;
454: }
455:
456: }
|