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: BlockingRepository.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.rep;
009:
010: import com.uwyn.rife.ioc.HierarchicalProperties;
011: import com.uwyn.rife.rep.exceptions.BlockingParticipantExpectedException;
012: import com.uwyn.rife.rep.exceptions.InitializationErrorException;
013: import com.uwyn.rife.rep.exceptions.RepException;
014: import com.uwyn.rife.resources.ResourceFinder;
015: import com.uwyn.rife.resources.ResourceFinderClasspath;
016: import com.uwyn.rife.tools.ExceptionUtils;
017: import com.uwyn.rife.xml.exceptions.XmlErrorException;
018: import java.util.ArrayList;
019: import java.util.Collection;
020: import java.util.HashMap;
021: import java.util.HashSet;
022: import java.util.logging.Logger;
023:
024: /**
025: * The <code>BlockingRepository</code> class provides a
026: * <code>Repository</code> implementation that loads the participants from an
027: * XML file.
028: * <p>This file defaults to <code>rep/participants.xml</code>, but it can be
029: * overridden by providing another filename to the <code>{@link
030: * #initialize(String, ResourceFinder) initialize}</code> method. The
031: * participants are initialized according to their listed order.
032: * <p>Following is an example of such an XML file :
033: * <pre><?xml version="1.0" encoding="UTF-8"?>
034: *<!DOCTYPE rep SYSTEM "/dtd/rep.dtd">
035: *<rep>
036: *<participant blocking="true" parameter="rep/config.xml">ParticipantConfig</participant>
037: *<participant blocking="false" parameter="graphics/buttons/">ParticipantImages</participant>
038: *<participant name="my cursors" blocking="false" parameter="graphics/cursors/">ParticipantCursors</participant>
039: *</rep></pre>
040: * <p>Each participant has a <code>blocking</code> attribute that determines
041: * whether the repository should wait for the end of the participant's
042: * initialization before progressing to the next participant or not. Using
043: * this intelligently, it's possible to dramatically increase the perceived
044: * startup time of an application.
045: * <p>Optionally a participant can have a <code>name</code> attribute which
046: * makes it possible to declare multiple participants of the same class. If no
047: * name is provided, the participant's class name will be used to identify the
048: * declared participant.
049: * <p>Optionally a participant can also have a <code>parameter</code>
050: * attribute which is merely a <code>String</code> that is provided to the
051: * participant object for configuration purposes.
052: * <p>Listeners can be added to the repository to receive notifications about
053: * the initialization advancement of the participants and to know when the
054: * initialization has completely finished. These notifications can, for
055: * example, be used to display a progress bar in a splash window and to switch
056: * to the real application window when the initialization has finished.
057: * <p>The JDK's logging facility is used to output informative text during the
058: * advancement of the initialization. Each participant has to provide an
059: * initialization message that will be output.
060: *
061: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
062: * @version $Revision: 3634 $
063: * @see RepositoryListener
064: * @see BlockingParticipant
065: * @since 1.0
066: */
067: public class BlockingRepository extends ThreadGroup implements
068: Repository {
069: /**
070: * The default path of the repository participant XML file is
071: * <code>rep/participants.xml</code>. It can be overridden by setting the
072: * <code>rep.path</code> system property.
073: */
074: private static final String DEFAULT_REP_PATH = "rep/participants.xml";
075:
076: /**
077: * A map of the registered participants, indexed according to their name.
078: */
079: private HashMap<String, BlockingParticipant> mRepParticipants = new HashMap<String, BlockingParticipant>();
080: /**
081: * A map of the registered participants class name with all the registered
082: * participants.
083: */
084: private HashMap<String, ArrayList<BlockingParticipant>> mRepParticipantClassnames = new HashMap<String, ArrayList<BlockingParticipant>>();
085: /**
086: * A list of the participant names, ordered according to their
087: * registration moment. This will be used as the order of execution.
088: */
089: private ArrayList<String> mRepParticipantsOrder = new ArrayList<String>();
090: /**
091: * The list of participant names that will be use to determine which
092: * participant's initialization end the repository has to wait for.
093: */
094: private ArrayList<String> mRepParticipantsToWaitFor = new ArrayList<String>();
095: /**
096: * The list of all registered <code>RepListener</code> objects.
097: */
098: private HashSet<RepositoryListener> mRepListeners = new HashSet<RepositoryListener>();
099: /**
100: * Indicates whether the initialization is finished.
101: */
102: private boolean mFinished = false;
103: /**
104: * Object that is used for thread synchronization of the finish.
105: */
106: private final Object mFinishedThreadMonitor = new Object();
107: /**
108: * Indicates whether the repository has been cleaned up.
109: */
110: private boolean mCleanedUp = false;
111: /**
112: * Contains a handle to the registered shutdown hook.
113: */
114: private BlockingRepositoryCleanup mCleanupShutdownHook = null;
115: /**
116: * Object that is used for thread synchronization of the cleanup.
117: */
118: private final Object mCleanupThreadMonitor = new Object();
119: /**
120: * Exception that is thrown by a participant.
121: */
122: private Throwable mParticipantException = null;
123: /**
124: * The repository's properties.
125: */
126: private HierarchicalProperties mProperties;
127: /**
128: * The repository's context.
129: */
130: private Object mContext;
131:
132: /**
133: * Default constructor without a repository context.
134: */
135: public BlockingRepository() {
136: this (null);
137: }
138:
139: /**
140: * Constructor which sets up a the context in which the repository is initialized.
141: */
142: public BlockingRepository(Object context) {
143: super ("BlockingRepository");
144:
145: HierarchicalProperties system_properties = new HierarchicalProperties()
146: .putAll(System.getProperties());
147: mProperties = new HierarchicalProperties()
148: .parent(system_properties);
149: mContext = context;
150: }
151:
152: /**
153: * Adds a <code>BlockingParticipant</code> to the repository, using the
154: * class name for the name of the participant.
155: * <p>The participant will not be blocking and have no parameter.
156: *
157: * @param className The fully resolved name of the participant's class, or
158: * only the class name if the participant resides in the
159: * <code>com.uwyn.rife.rep.participants</code> package.
160: * @return <code>true</code> if the participants was added successfully;
161: * or
162: * <p><code>false</code> if errors occurred
163: * @see BlockingParticipant
164: * @see Participant
165: * @see #addParticipant(Class, String, boolean, String)
166: * @since 1.5
167: */
168: public boolean addParticipant(String className) throws RepException {
169: return addParticipant(className, null, false, null);
170: }
171:
172: /**
173: * Adds a <code>BlockingParticipant</code> to the repository, using the
174: * class name for the name of the participant.
175: * <p>The participant will have no parameter.
176: *
177: * @param className The fully resolved name of the participant's class, or
178: * only the class name if the participant resides in the
179: * <code>com.uwyn.rife.rep.participants</code> package.
180: * @param blocking Indicates if this a blocking participant or not.
181: * @return <code>true</code> if the participants was added successfully;
182: * or
183: * <p><code>false</code> if errors occurred
184: * @see BlockingParticipant
185: * @see Participant
186: * @see #addParticipant(Class, String, boolean, String)
187: * @since 1.5
188: */
189: public boolean addParticipant(String className, boolean blocking)
190: throws RepException {
191: return addParticipant(className, null, blocking, null);
192: }
193:
194: /**
195: * Adds a <code>BlockingParticipant</code> to the repository, using the
196: * class name for the name of the participant.
197: * <p>The participant will not be blocking.
198: *
199: * @param className The fully resolved name of the participant's class, or
200: * only the class name if the participant resides in the
201: * <code>com.uwyn.rife.rep.participants</code> package.
202: * @param parameter An optional string that contains the parameter for
203: * this participant.
204: * @return <code>true</code> if the participants was added successfully;
205: * or
206: * <p><code>false</code> if errors occurred
207: * @see BlockingParticipant
208: * @see Participant
209: * @see #addParticipant(Class, String, boolean, String)
210: * @since 1.5
211: */
212: public boolean addParticipant(String className, String parameter)
213: throws RepException {
214: return addParticipant(className, null, false, parameter);
215: }
216:
217: /**
218: * Adds a <code>BlockingParticipant</code> to the repository, using the
219: * class name for the name of the participant.
220: *
221: * @param className The fully resolved name of the participant's class, or
222: * only the class name if the participant resides in the
223: * <code>com.uwyn.rife.rep.participants</code> package.
224: * @param blocking Indicates if this a blocking participant or not.
225: * @param parameter An optional string that contains the parameter for
226: * this participant.
227: * @return <code>true</code> if the participants was added successfully;
228: * or
229: * <p><code>false</code> if errors occurred
230: * @see BlockingParticipant
231: * @see Participant
232: * @see #addParticipant(Class, String, boolean, String)
233: * @since 1.5
234: */
235: public boolean addParticipant(String className, boolean blocking,
236: String parameter) throws RepException {
237: return addParticipant(className, null, blocking, parameter);
238: }
239:
240: /**
241: * Adds a <code>BlockingParticipant</code> to the repository.
242: *
243: * @param className The fully resolved name of the participant's class, or
244: * only the class name if the participant resides in the
245: * <code>com.uwyn.rife.rep.participants</code> package.
246: * @param name The name under which the participant will be registered, if
247: * the name is <code>null</code> the class name will be used
248: * @param blocking Indicates if this a blocking participant or not.
249: * @param parameter An optional string that contains the parameter for
250: * this participant.
251: * @return <code>true</code> if the participants was added successfully;
252: * or
253: * <p><code>false</code> if errors occurred
254: * @see BlockingParticipant
255: * @see Participant
256: * @see #addParticipant(Class, String, boolean, String)
257: * @since 1.0
258: */
259: public boolean addParticipant(String className, String name,
260: boolean blocking, String parameter) throws RepException {
261: // Try to resolve the participant's classname to ensure that an
262: // object can be instantiated.
263: Class klass = null;
264: try {
265: klass = Class.forName(className);
266: } catch (ClassNotFoundException e1) {
267: className = "com.uwyn.rife.rep.participants." + className;
268: try {
269: klass = Class.forName(className);
270: } catch (ClassNotFoundException e2) {
271: return false;
272: }
273: }
274:
275: return addParticipant(klass, name, blocking, parameter);
276: }
277:
278: /**
279: * Adds a <code>BlockingParticipant</code> to the repository, using the
280: * class name as the name of the participant.
281: *
282: * @param klass The class of the participant.
283: * @param blocking Indicates if this a blocking participant or not.
284: * @param parameter An optional string that contains the parameter for
285: * this participant.
286: * @return <code>true</code> if the participants was added successfully;
287: * or
288: * <p><code>false</code> if errors occurred
289: * @see BlockingParticipant
290: * @see Participant
291: * @see #addParticipant(Class, String, boolean, String)
292: * @since 1.5
293: */
294: public boolean addParticipant(Class klass, boolean blocking,
295: String parameter) throws RepException {
296: return addParticipant(klass, null, blocking, parameter);
297: }
298:
299: /**
300: * Adds a <code>BlockingParticipant</code> to the repository, using the
301: * class name as the name of the participant.
302: * <p>The participant will not be blocking and have no parameter.
303: *
304: * @param klass The class of the participant.
305: * this participant.
306: * @return <code>true</code> if the participants was added successfully;
307: * or
308: * <p><code>false</code> if errors occurred
309: * @see BlockingParticipant
310: * @see Participant
311: * @see #addParticipant(Class, String, boolean, String)
312: * @since 1.5
313: */
314: public boolean addParticipant(Class klass) throws RepException {
315: return addParticipant(klass, null, false, null);
316: }
317:
318: /**
319: * Adds a <code>BlockingParticipant</code> to the repository, using the
320: * class name as the name of the participant.
321: * <p>The participant will have no parameter.
322: *
323: * @param klass The class of the participant.
324: * @param blocking Indicates if this a blocking participant or not.
325: * @return <code>true</code> if the participants was added successfully;
326: * or
327: * <p><code>false</code> if errors occurred
328: * @see BlockingParticipant
329: * @see Participant
330: * @see #addParticipant(Class, String, boolean, String)
331: * @since 1.5
332: */
333: public boolean addParticipant(Class klass, boolean blocking)
334: throws RepException {
335: return addParticipant(klass, null, blocking, null);
336: }
337:
338: /**
339: * Adds a <code>BlockingParticipant</code> to the repository, using the
340: * class name as the name of the participant.
341: * <p>The participant will not be blocking.
342: *
343: * @param klass The class of the participant.
344: * @param parameter An optional string that contains the parameter for
345: * this participant.
346: * @return <code>true</code> if the participants was added successfully;
347: * or
348: * <p><code>false</code> if errors occurred
349: * @see BlockingParticipant
350: * @see Participant
351: * @see #addParticipant(Class, String, boolean, String)
352: * @since 1.5
353: */
354: public boolean addParticipant(Class klass, String parameter)
355: throws RepException {
356: return addParticipant(klass, null, false, parameter);
357: }
358:
359: /**
360: * Adds a <code>BlockingParticipant</code> to the repository.
361: *
362: * @param klass The class of the participant.
363: * @param name The name under which the participant will be registered, if
364: * the name is <code>null</code> the class name will be used
365: * @param blocking Indicates if this a blocking participant or not.
366: * @param parameter An optional string that contains the parameter for
367: * this participant.
368: * @return <code>true</code> if the participants was added successfully;
369: * or
370: * <p><code>false</code> if errors occurred
371: * @see BlockingParticipant
372: * @see Participant
373: * @since 1.0
374: */
375: public boolean addParticipant(Class klass, String name,
376: boolean blocking, String parameter) throws RepException {
377: boolean generated_name = false;
378:
379: if (null == name || 0 == name.length()) {
380: name = klass.getName();
381: generated_name = true;
382: }
383:
384: // Check if the participant isn't already present in the repository.
385: if (mRepParticipants.containsKey(name)) {
386: if (!generated_name) {
387: return false;
388: }
389:
390: String new_name = null;
391: int counter = 2;
392: do {
393: new_name = name + counter;
394: counter++;
395: } while (mRepParticipants.containsKey(new_name));
396:
397: name = new_name;
398: }
399:
400: try {
401: // Try to create an instance of the participant.
402: BlockingParticipant participant_instance = null;
403: if (!BlockingParticipant.class.isAssignableFrom(klass)) {
404: if (Participant.class.isAssignableFrom(klass)) {
405: participant_instance = new BlockingParticipantDelegate(
406: klass);
407: } else {
408: throw new BlockingParticipantExpectedException(
409: klass.getName());
410: }
411: } else {
412: participant_instance = (BlockingParticipant) klass
413: .newInstance();
414: }
415:
416: // Sets the name of the blocking participant thread to the one
417: // it will be registered with
418: participant_instance.setName(name);
419:
420: // Setup the thread
421: Thread thread = new Thread(this , participant_instance);
422: participant_instance.setThread(thread);
423:
424: // Store the participant and its name, remember it's execution order
425: // according to other participants and remember whether this
426: // participant's initialization should be finished before
427: // continuing the repository initialization.
428: // Regardless of their names, all the participants are already
429: // registered with their class name.
430: // Store its optional parameter.
431: participant_instance.setRepository(this );
432: mRepParticipants.put(name, participant_instance);
433: ArrayList<BlockingParticipant> participants = mRepParticipantClassnames
434: .get(klass.getName());
435: if (null == participants) {
436: participants = new ArrayList<BlockingParticipant>();
437: mRepParticipantClassnames.put(klass.getName(),
438: participants);
439: }
440: participants.add(participant_instance);
441: mRepParticipantsOrder.add(name);
442: if (blocking) {
443: mRepParticipantsToWaitFor.add(name);
444: }
445: if (null != parameter) {
446: participant_instance.setParameter(parameter);
447: }
448: return true;
449: } catch (IllegalAccessException e) {
450: return false;
451: } catch (InstantiationException e) {
452: return false;
453: }
454: }
455:
456: /**
457: * Verifies if a participant that corresponds to a given name is present.
458: *
459: * @param name The name of the participant object that you wish to
460: * retrieve from the repository. See the {@link #getParticipant(String)
461: * getParticipant} method for detailed information about how the
462: * participant's name is resolved.
463: * @return <code>true</code> if the provided class name could be found, or
464: * <p><code>false</code> otherwise
465: * @see BlockingParticipant
466: * @see #getParticipant(String)
467: * @since 1.0
468: */
469: public boolean hasParticipant(String name) {
470: BlockingParticipant participant = getParticipant(name);
471: if (null == participant) {
472: return false;
473: }
474:
475: if (participant.hadInitializationError()) {
476: return false;
477: }
478:
479: return true;
480: }
481:
482: /**
483: * Looks for the participant that corresponds to a given name and returns
484: * it when found.
485: *
486: * @param name The name of the participant instance that you wish to
487: * retrieve from the repository.
488: * <p>If no name was provided during the XML specification, the
489: * participant will have been registered with its class name. If the
490: * participant's class is not part of the
491: * <code>com.uwyn.rife.rep.participants</code> package, its full class
492: * name has to be provided, otherwise just the name of the class itself is
493: * sufficient.
494: * <p>Also, even though a participant has been registered with a name,
495: * it'll still be known under its class name. When a class name is
496: * provided as the argument, the first known participant of that class
497: * will be returned. This can be seen as the default participant for the
498: * specified type.
499: * @return A <code>BlockingParticipant</code> instance if the provided
500: * name could be found amongst the registered participants in the
501: * repository; or
502: * <p><code>null</code> if the participant couldn't be found
503: * @see BlockingParticipant
504: * @see #hasParticipant(String)
505: * @since 1.0
506: */
507: public BlockingParticipant getParticipant(String name) {
508: // check if the provided participant name is present in the repository
509: if (mRepParticipants.containsKey(name)) {
510: return mRepParticipants.get(name);
511: }
512:
513: // check if the provided participant name can be expanded to a name that
514: // is known by the repository
515: String prefixed_name = "com.uwyn.rife.rep.participants." + name;
516: if (mRepParticipants.containsKey(prefixed_name)) {
517: return mRepParticipants.get(prefixed_name);
518: }
519:
520: // check if the provided participant name has been remembered as the
521: // first participant of a certain class
522: if (mRepParticipantClassnames.containsKey(name)) {
523: ArrayList<BlockingParticipant> participants = mRepParticipantClassnames
524: .get(name);
525: if (participants.size() > 0) {
526: return participants.get(0);
527: }
528: }
529: if (mRepParticipantClassnames.containsKey(prefixed_name)) {
530: ArrayList<BlockingParticipant> participants = mRepParticipantClassnames
531: .get(prefixed_name);
532: if (participants.size() > 0) {
533: return participants.get(0);
534: }
535: }
536:
537: return null;
538: }
539:
540: /**
541: * Returns all the participants with a given class name
542: *
543: * @param className The class name of the participants that you wish to
544: * retrieve from the repository.
545: * <p>If the participant's class is not part of the
546: * <code>com.uwyn.rife.rep.participants</code> package, its full class
547: * name has to be provided, otherwise just the name of the class itself is
548: * sufficient.
549: * @return A <code>Collection</code> of <code>BlockingParticipant</code>
550: * instances of the provided class name; or
551: * <p><code>null</code> if no participants with the provided class name
552: * could be found
553: * @see BlockingParticipant
554: * @see #getParticipant(String)
555: * @since 1.0
556: */
557: public Collection<BlockingParticipant> getParticipants(
558: String className) {
559: // check if the provided participant name has been remembered as the
560: // first participant of a certain class
561: if (mRepParticipantClassnames.containsKey(className)) {
562: ArrayList<BlockingParticipant> participants = mRepParticipantClassnames
563: .get(className);
564: if (participants.size() > 0) {
565: return participants;
566: }
567: }
568: // check if the provided participant class name can be expanded to a
569: // name that is known by the repository
570: String prefixed_name = "com.uwyn.rife.rep.participants."
571: + className;
572: if (mRepParticipantClassnames.containsKey(prefixed_name)) {
573: ArrayList<BlockingParticipant> participants = mRepParticipantClassnames
574: .get(prefixed_name);
575: if (participants.size() > 0) {
576: return participants;
577: }
578: }
579:
580: return null;
581: }
582:
583: /**
584: * Sequentially execute the participants according to their registration
585: * order. If the participant has already been run or is still running, it
586: * is not executed anymore. The repository waits for the participant's
587: * execution to finish if this has been indicated by registering with the
588: * <code>blocking</code> attribute.
589: * <p>The resource finder that will be used is an instance of {@link ResourceFinderClasspath}.
590: *
591: * @see ResourceFinderClasspath
592: * @see #runParticipants(ResourceFinder)
593: * @since 1.5
594: */
595: public void runParticipants() {
596: runParticipants(ResourceFinderClasspath.getInstance());
597: }
598:
599: /**
600: * Sequentially execute the participants according to their registration
601: * order. If the participant has already been run or is still running, it
602: * is not executed anymore. The repository waits for the participant's
603: * execution to finish if this has been indicated by registering with the
604: * <code>blocking</code> attribute.
605: *
606: * @param resourceFinder The resource finder that is used during the
607: * initialization.
608: * @since 1.0
609: */
610: public void runParticipants(ResourceFinder resourceFinder) {
611: // Iterate over the participants according to their registration order.
612: for (String name : mRepParticipantsOrder) {
613: BlockingParticipant participant = mRepParticipants
614: .get(name);
615: try {
616: // Only start the participant's initialization if it hasn't run
617: // before.
618: if (!participant.getThread().isAlive()
619: && !participant.isFinished()) {
620: Logger
621: .getLogger("com.uwyn.rife.rep")
622: .info(
623: "INITIALIZATION : "
624: + participant
625: .getInitializationMessage());
626: // Initialize the participant
627: participant.setResourceFinder(resourceFinder);
628: participant.getThread().start();
629: // Wait for the initialization to finish if this has been
630: // specified.
631: if (mRepParticipantsToWaitFor.contains(name)) {
632: participant.waitUntilFinished();
633: }
634: }
635: } finally {
636: detectParticipantException();
637: }
638: }
639:
640: synchronized (mFinishedThreadMonitor) {
641: if (!mFinished) {
642: mCleanupShutdownHook = new BlockingRepositoryCleanup(
643: this );
644: Runtime.getRuntime().addShutdownHook(
645: mCleanupShutdownHook);
646: mFinished = true;
647: fireInitFinished();
648: }
649: }
650: }
651:
652: private void detectParticipantException() {
653: if (mParticipantException != null) {
654: if (mParticipantException instanceof RuntimeException) {
655: throw (RuntimeException) mParticipantException;
656: }
657: throw new RuntimeException(mParticipantException);
658: }
659: }
660:
661: /**
662: * If participants call an exception, clean up correctly and rethrow the
663: * exception afterwards.
664: *
665: * @since 1.0
666: */
667: public void uncaughtException(Thread thread, Throwable e) {
668: if (e instanceof ThreadDeath) {
669: super .uncaughtException(thread, e);
670: }
671:
672: if (null == mParticipantException) {
673: mParticipantException = e;
674: }
675:
676: synchronized (mFinishedThreadMonitor) {
677: if (!mFinished) {
678: mFinished = true;
679: fireInitFinished();
680: cleanup();
681: }
682: // the repository has finished so log the exception
683: else {
684: if (mParticipantException != null) {
685: Logger
686: .getLogger("com.uwyn.rife.rep")
687: .severe(
688: ExceptionUtils
689: .getExceptionStackTrace(mParticipantException));
690: }
691: }
692: }
693: }
694:
695: /**
696: * Parses the XML file to determine what the participants are. Then, one
697: * by one, initializes each participant with the {@link
698: * #runParticipants(ResourceFinder) runParticipants} method, waiting for
699: * it to finish if its <code>blocking</code> attribute was set to
700: * <code>true</code>.
701: *
702: * @param repXmlPath The path of the XML file.
703: * <p>If this is <code>null</code>, <code>rep/participants.xml</code> will
704: * be used.
705: * @param resourcefinder The resource finder that will be used to look up
706: * resources such as XML files and DTDs. It will also be used by other
707: * classes after initialization through the
708: * <code>getResourceFinder()</code> method.
709: * <p>If this is <code>null</code>, an instance of <code>{@link
710: * ResourceFinderClasspath}</code> will be used.
711: * @exception RepException when an error occurs during the initialization.
712: * @since 1.0
713: */
714: public void initialize(String repXmlPath,
715: ResourceFinder resourcefinder) throws RepException {
716: mFinished = false;
717:
718: if (null == resourcefinder) {
719: resourcefinder = ResourceFinderClasspath.getInstance();
720: }
721: if (null == repXmlPath) {
722: repXmlPath = DEFAULT_REP_PATH;
723: }
724:
725: // Parse the repository configuration file.
726: Xml2BlockingRepository xml2rep = new Xml2BlockingRepository(
727: this );
728: try {
729: // Add the participants that have been specified in the
730: // configuration file to the repository.
731: xml2rep.addRepParticipants(repXmlPath, resourcefinder);
732: }
733: // If errors occured during the parsing of the repository configuration
734: // file, output a message and throw a runtime error.
735: catch (XmlErrorException e) {
736: throw new InitializationErrorException(e);
737: }
738:
739: // Initialize the participants.
740: runParticipants(resourcefinder);
741: }
742:
743: /**
744: * Obtains the finished status of the initialization.
745: *
746: * @return <code>false</code> if the initialization is still busy; or
747: * <p><code>true</code> if the initialization is finished
748: * @since 1.0
749: */
750: public boolean isFinished() {
751: return mFinished;
752: }
753:
754: /**
755: * Cleans up the participants in the order in which they have been
756: * declared. Every participant's <code>cleanup()</code> method is
757: * successively called.
758: *
759: * @exception RepException when an error occurs during the cleanup.
760: * @since 1.0
761: */
762: public void cleanup() throws RepException {
763: if (!mFinished) {
764: return;
765: }
766:
767: synchronized (mCleanupThreadMonitor) {
768: if (mCleanedUp) {
769: return;
770: }
771:
772: // iterate over all the registered participants in their reverse
773: // registration order
774: String name = null;
775: BlockingParticipant participant = null;
776: for (int i = mRepParticipantsOrder.size() - 1; i >= 0; i--) {
777: name = mRepParticipantsOrder.get(i);
778:
779: // obtain each participant by its name and clean it up
780: participant = getParticipant(name);
781: if (participant != null) {
782: Logger.getLogger("com.uwyn.rife.rep").info(
783: "CLEANUP : "
784: + participant.getCleanupMessage());
785: participant.cleanup();
786: }
787: }
788:
789: mCleanedUp = true;
790: }
791: }
792:
793: public HierarchicalProperties getProperties() {
794: return mProperties;
795: }
796:
797: public Object getContext() {
798: return mContext;
799: }
800:
801: /**
802: * Adds the specified repository listener to receive repository
803: * initialization events. If <code>repListener</code> is null, no
804: * exception is thrown and no action is performed.
805: *
806: * @param repListener The repository listener that will be added.
807: * @see RepositoryListener
808: * @see #removeRepListener(RepositoryListener)
809: * @since 1.0
810: */
811: public void addRepListener(RepositoryListener repListener) {
812: if (null == repListener)
813: return;
814:
815: mRepListeners.add(repListener);
816: }
817:
818: /**
819: * Removes the repository listener so that it no longer receives
820: * repository initialization events. This method performs no function, nor
821: * does it throw an exception, if the listener specified by the argument
822: * was not previously added to this component. If <code>repListener</code>
823: * is <code>null</code>, no exception is thrown and no action is
824: * performed.
825: *
826: * @param repListener The repository listener that will be removed.
827: * @see RepositoryListener
828: * @see #addRepListener(RepositoryListener)
829: * @since 1.0
830: */
831: public void removeRepListener(RepositoryListener repListener) {
832: mRepListeners.remove(repListener);
833: }
834:
835: /**
836: * Notifies the registered listeners that a new initialization action has
837: * been performed.
838: * <p>This is always triggered when a participant's initialization has
839: * finished. Each participant however has the possibility to call this
840: * method directly, allowed for finer-grained notification of the
841: * advancement of the initialization.
842: *
843: * @param participant The participant that triggered the action.
844: * @see RepositoryListener
845: * @since 1.0
846: */
847: public void fireInitActionPerformed(BlockingParticipant participant) {
848: if (mRepListeners.size() > 0) {
849: for (RepositoryListener listener : mRepListeners) {
850: listener.initActionPerformed(participant);
851: }
852: }
853: }
854:
855: /**
856: * Notifies the registered listeners that the repository initialization
857: * has finished.
858: *
859: * @see RepositoryListener
860: * @since 1.0
861: */
862: public void fireInitFinished() {
863: if (mRepListeners.size() > 0) {
864: for (RepositoryListener listener : mRepListeners) {
865: listener.initFinished();
866: }
867: }
868: }
869: }
|