001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: BlockingParticipant.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.rep;
009:
010: import com.uwyn.rife.resources.ResourceFinder;
011:
012: /**
013: * A repository participant is basically a service that needs to be
014: * initialized before it can return objects that correspond to specified
015: * identification keys.
016: * <p>Each participant is launched in a seperate thread which is started to
017: * perform the initialization. This thread can run in parallel with the
018: * initializations of other participants. Whether this is the case is
019: * determined by the repository through the <code>blocking</code> parameter
020: * that has to be provided during the registration of the participant with the
021: * repository.
022: *
023: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
024: * @version $Revision: 3634 $
025: * @see Rep
026: * @since 1.0
027: */
028: public abstract class BlockingParticipant implements Participant,
029: Runnable {
030: private BlockingRepository mRepository = null;
031: /**
032: * Object that is used for thread synchronization of the initialization.
033: */
034: private final Object mInitThreadMonitor = new Object();
035: /**
036: * Text message is that retrieved by the repository to describe the
037: * function of a particular participant during the initialization.
038: */
039: private String mInitializationMessage = null;
040: /**
041: * Text message is that retrieved by the repository to describe the
042: * function of a particular participant during the cleanup.
043: */
044: private String mCleanupMessage = null;
045: /**
046: * Instance of the <code>ResourceFinder</code> class that is used by the
047: * repository when it initializes its participants. This resource finder
048: * is only accessible by sub classes through the
049: * <code>getResourceFinder()</code> method.
050: */
051: private ResourceFinder mResourcefinder = null;
052: /**
053: * Indicates whether the initialization of this participant has finished
054: * or not.
055: */
056: private volatile boolean mInitializationFinished = false;
057: /**
058: * Indicates whether the initialization of this participant throw an error
059: */
060: private volatile boolean mInitializationError = false;
061: /**
062: * The parameter that has been specified in the configuration file.
063: */
064: private String mParameter = null;
065: /**
066: * The name of the thread in which the participant will run.
067: */
068: private String mThreadName = null;
069: /**
070: * The thread instance in which the participant will run.
071: */
072: private Thread mThread = null;
073:
074: /**
075: * Sets the name of the thread.
076: *
077: * @param name The name of the thread.
078: * @since 1.0
079: */
080: void setName(String name) {
081: mThreadName = name;
082:
083: if (mThread != null) {
084: mThread.setName(mThreadName);
085: }
086: }
087:
088: /**
089: * Retrieves the name of the thread.
090: *
091: * @since 1.0
092: */
093: public String getName() {
094: if (mThread != null) {
095: return mThread.getName();
096: }
097:
098: return mThreadName;
099: }
100:
101: /**
102: * Performs the actual initialization actions for the participant. This is
103: * an abstract method that needs to be implemented by every participant.
104: *
105: * @since 1.0
106: */
107: protected abstract void initialize();
108:
109: /**
110: * Does the actual retrieval of an object from the participant according
111: * to a specified key. This method needs to be implemented by every
112: * participant that provides access to data, by default it just returns
113: * <code>null</code>.
114: *
115: * @param key An <code>Object</code> instance that is used as the key to
116: * obtain a corresponding object from the participant with.
117: * @return <code>null</code> if no object could be found that corresponds
118: * to the provided key; or
119: * <p>an <code>Object</code> instance that corresponds to the provided key
120: * @see #getObject()
121: * @see #getObject(Object)
122: * @since 1.0
123: */
124: protected Object _getObject(Object key) {
125: return null;
126: }
127:
128: /**
129: * Performs the actual cleanup actions for the participant. This is method
130: * can be overridden when a participant needs to customize the cleanup..
131: *
132: * @since 1.0
133: */
134: protected void cleanup() {
135: }
136:
137: /**
138: * Sets the repository that this participant belongs to.
139: *
140: * @param repository an instance of <code>BlockingRepository</code>
141: * @see BlockingRepository
142: * @since 1.0
143: */
144: void setRepository(BlockingRepository repository) {
145: mRepository = repository;
146: }
147:
148: /**
149: * Retrieves the repository that this participant belongs to.
150: *
151: * @since 1.0
152: */
153: public BlockingRepository getRepository() {
154: return mRepository;
155: }
156:
157: /**
158: * Sets the optional parameter.
159: *
160: * @param parameter A <code>String</code> containing the optional
161: * parameter for this participant.
162: * @since 1.0
163: */
164: public void setParameter(String parameter) {
165: mParameter = parameter;
166: }
167:
168: /**
169: * Retrieves the optional parameter.
170: *
171: * @return <code>null</code> if no parameter was provided; or
172: * <p>the requested parameter <code>String</code> instance otherwise
173: * @since 1.0
174: */
175: public String getParameter() {
176: return mParameter;
177: }
178:
179: /**
180: * Sets the resource finder that can be used during the
181: * <code>initialize()</code> method.
182: *
183: * @param resourceFinder A <code>ResourceFinder</code> instance containing
184: * the resource finder that is used during the initialization of the
185: * repository and its participants.
186: * @see #getResourceFinder()
187: * @since 1.0
188: */
189: public void setResourceFinder(ResourceFinder resourceFinder) {
190: mResourcefinder = resourceFinder;
191: }
192:
193: /**
194: * Retrieves the resource finder that is used during the initialization.
195: *
196: * @return <code>null</code> if no resource finder was provided or if the
197: * method was called after the initialization; or
198: * <p>the requested <code>ResourceFinder</code> instance otherwise
199: * @see #setResourceFinder(ResourceFinder)
200: * @since 1.0
201: */
202: public ResourceFinder getResourceFinder() {
203: return mResourcefinder;
204: }
205:
206: /**
207: * Starts the initialization.
208: *
209: * @since 1.0
210: */
211: public final void run() {
212: // Only initialize once.
213: if (!isFinished()) {
214: try {
215: initialize();
216: } catch (Throwable e) {
217: mInitializationError = true;
218: getThread().getThreadGroup().uncaughtException(mThread,
219: e);
220: } finally {
221: // Obtain a lock on the synchronization monitor of this particular
222: // participant to make it possible to notify all waiting threads.
223: synchronized (mInitThreadMonitor) {
224: mInitializationFinished = true;
225: mInitThreadMonitor.notifyAll();
226: mRepository.fireInitActionPerformed(this );
227: }
228: }
229: }
230: }
231:
232: /**
233: * Checks if the initialization of this participant is finished.
234: *
235: * @return <code>true</code> if the initialization is finished; or
236: * <p><code>false</code> if the initialization is in progress
237: * @since 1.0
238: */
239: public final boolean isFinished() {
240: return mInitializationFinished;
241: }
242:
243: /**
244: * Checks if the initialization of this participant threw an error.
245: *
246: * @return <code>true</code> if the initialization threw an error; or
247: * <p><code>false</code> if the initialization was successful
248: * @since 1.0
249: */
250: public boolean hadInitializationError() {
251: synchronized (mInitThreadMonitor) {
252: return mInitializationError;
253: }
254: }
255:
256: /**
257: * Makes the calling thread wait until the initialization of this
258: * participant has finished.
259: *
260: * @since 1.0
261: */
262: public final void waitUntilFinished() {
263: // Obtain a lock on the synchronization monitor of this particular
264: // participant to make it possible to wait for notifications on this
265: // monitor.
266: synchronized (mInitThreadMonitor) {
267: // Only make the calling thread wait if the initialization is still
268: // busy.
269: while (!isFinished()) {
270: try {
271: mInitThreadMonitor.wait();
272: } catch (InterruptedException e) {
273: }
274: }
275: }
276: }
277:
278: /**
279: * Overrides the default message that describes the initialization of this
280: * participant.
281: *
282: * @param message A <code>String</code> containing the message.
283: * @see #getInitializationMessage()
284: * @since 1.0
285: */
286: public void setInitializationMessage(String message) {
287: mInitializationMessage = message;
288: }
289:
290: /**
291: * Returns a message that is supposed to describe the initialization of
292: * this participant. If no message has been set with <code>setInitializationMessage(String
293: * message)</code>, a default message is generated.
294: *
295: * @return A <code>String</code> containing the message that describes the
296: * initialization of this participant.
297: * @see #setInitializationMessage(String)
298: * @since 1.0
299: */
300: public String getInitializationMessage() {
301: if (null == mInitializationMessage) {
302: return "Initializing '" + this .getClass().getName()
303: + "' ...";
304: } else {
305: return mInitializationMessage;
306: }
307: }
308:
309: /**
310: * Overrides the default message that describes the cleanup of this
311: * participant.
312: *
313: * @param message A <code>String</code> containing the message.
314: * @see #getCleanupMessage()
315: * @since 1.0
316: */
317: public void setCleanupMessage(String message) {
318: mCleanupMessage = message;
319: }
320:
321: /**
322: * Returns a message that is supposed to describe the cleanup of this
323: * participant. If no message has been set with <code>setCleanupMessage(String
324: * message)</code>, a default message is generated.
325: *
326: * @return A <code>String</code> containing the message that describes the
327: * cleanup of this participant.
328: * @see #setCleanupMessage(String)
329: * @since 1.0
330: */
331: public String getCleanupMessage() {
332: if (null == mCleanupMessage) {
333: return "Cleaning up '" + this .getClass().getName()
334: + "' ...";
335: } else {
336: return mCleanupMessage;
337: }
338: }
339:
340: /**
341: * Returns the default object for this participant.
342: * <p>If the initialization of the participant hasn't finished yet, the
343: * thread that executes this method will be suspended and woken up when
344: * the initialization finishes.
345: *
346: * @return <code>null</code> if no default object exists; or
347: * <p>an <code>Object</code> instance containing the default object
348: * @see #getObject(Object)
349: * @see #_getObject(Object)
350: * @since 1.0
351: */
352: public final Object getObject() {
353: // if the participant is finished, return the object directly
354: if (isFinished()) {
355: return _getObject();
356: }
357:
358: return getObjectAndWait();
359: }
360:
361: private Object getObjectAndWait() {
362: // Obtain a lock on the synchronization monitor of this particular
363: // participant to make it possible to wait for notifications on this
364: // monitor.
365: synchronized (mInitThreadMonitor) {
366: // If the initialization hasn't finished, suspend the executing
367: // thread.
368: while (!isFinished()) {
369: try {
370: mInitThreadMonitor.wait();
371: } catch (InterruptedException e) {
372: // do nothing
373: }
374: }
375: return _getObject();
376: }
377: }
378:
379: protected Object _getObject() {
380: return getObject(null);
381: }
382:
383: /**
384: * Retrieves the object from the participant that corresponds to a
385: * particular key.
386: * <p>If the initialization of the participant hasn't finished yet, the
387: * thread that executes this method will be suspended and woken up when
388: * the initialization finishes.
389: *
390: * @param key An <code>Object</code> instance that used as the key to
391: * obtain a corresponding object from the participant with.
392: * @return <code>null</code> if no object could be found that corresponds
393: * to the provided key; or
394: * <p>the requested <code>Object</code> instance
395: * @see #getObject()
396: * @see #_getObject(Object)
397: * @since 1.0
398: */
399: public final Object getObject(Object key) {
400: // if the participant is finished, return the object directly
401: if (isFinished()) {
402: return _getObject(key);
403: }
404:
405: return getObjectAndWait(key);
406: }
407:
408: private Object getObjectAndWait(Object key) {
409: // Obtain a lock on the synchronization monitor of this particular
410: // participant to make it possible to wait for notifications on this
411: // monitor.
412: synchronized (mInitThreadMonitor) {
413: // If the initialization hasn't finished, suspend the executing
414: // thread.
415: while (!isFinished()) {
416: try {
417: mInitThreadMonitor.wait();
418: } catch (InterruptedException e) {
419: // do nothing
420: }
421: }
422: return _getObject(key);
423: }
424: }
425:
426: void setThread(Thread thread) {
427: mThread = thread;
428:
429: if (mThreadName != null) {
430: mThread.setName(mThreadName);
431: }
432: }
433:
434: Thread getThread() {
435: return mThread;
436: }
437: }
|