/* * Copyright 2001-2013 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") */ package com.uwyn.rife.rep; import com.uwyn.rife.resources.ResourceFinder; /** * A repository participant is basically a service that needs to be * initialized before it can return objects that correspond to specified * identification keys. * <p>Each participant is launched in a separate thread which is started to * perform the initialization. This thread can run in parallel with the * initializations of other participants. Whether this is the case is * determined by the repository through the <code>blocking</code> parameter * that has to be provided during the registration of the participant with the * repository. * * @author Geert Bevin (gbevin[remove] at uwyn dot com) * @see Rep * @since 1.0 */ public abstract class BlockingParticipant implements Participant, Runnable { /** * Object that is used for thread synchronization of the initialization. */ private final Object initThreadMonitor = new Object(); /** * The repository this participqnt belongs to. */ private BlockingRepository repository = null; /** * Text message is that retrieved by the repository to describe the * function of a particular participant during the initialization. */ private String initializationMessage = null; /** * Text message is that retrieved by the repository to describe the * function of a particular participant during the cleanup. */ private String cleanupMessage = null; /** * Instance of the <code>ResourceFinder</code> class that is used by the * repository when it initializes its participants. This resource finder * is only accessible by sub classes through the * <code>getResourceFinder()</code> method. */ private ResourceFinder resourcefinder = null; /** * Indicates whether the initialization of this participant has finished * or not. */ private volatile boolean initializationFinished = false; /** * Indicates whether the initialization of this participant throw an error */ private volatile boolean initializationError = false; /** * The parameter that has been specified in the configuration file. */ private String parameter = null; /** * The name of the thread in which the participant will run. */ private String threadName = null; /** * The thread instance in which the participant will run. */ private Thread thread = null; /** * Retrieves the name of the thread. * * @since 1.0 */ public String getName() { if (thread != null) { return thread.getName(); } return threadName; } /** * Sets the name of the thread. * * @param name The name of the thread. * @since 1.0 */ void setName(String name) { threadName = name; if (thread != null) { thread.setName(threadName); } } /** * Performs the actual initialization actions for the participant. This is * an abstract method that needs to be implemented by every participant. * * @since 1.0 */ protected abstract void initialize(); /** * Does the actual retrieval of an object from the participant according * to a specified key. This method needs to be implemented by every * participant that provides access to data, by default it just returns * <code>null</code>. * * @param key An <code>Object</code> instance that is used as the key to * obtain a corresponding object from the participant with. * @return <code>null</code> if no object could be found that corresponds * to the provided key; or * <p>an <code>Object</code> instance that corresponds to the provided key * @see #getObject() * @see #getObject(Object) * @since 1.0 */ protected Object _getObject(Object key) { return null; } /** * Performs the actual cleanup actions for the participant. This is method * can be overridden when a participant needs to customize the cleanup.. * * @since 1.0 */ protected void cleanup() { } /** * Retrieves the repository that this participant belongs to. * * @since 1.0 */ public BlockingRepository getRepository() { return repository; } /** * Sets the repository that this participant belongs to. * * @param repository an instance of <code>BlockingRepository</code> * @see BlockingRepository * @since 1.0 */ void setRepository(BlockingRepository repository) { this.repository = repository; } /** * Retrieves the optional parameter. * * @return <code>null</code> if no parameter was provided; or * <p>the requested parameter <code>String</code> instance otherwise * @since 1.0 */ public String getParameter() { return parameter; } /** * Sets the optional parameter. * * @param parameter A <code>String</code> containing the optional * parameter for this participant. * @since 1.0 */ public void setParameter(String parameter) { this.parameter = parameter; } /** * Retrieves the resource finder that is used during the initialization. * * @return <code>null</code> if no resource finder was provided or if the * method was called after the initialization; or * <p>the requested <code>ResourceFinder</code> instance otherwise * @see #setResourceFinder(ResourceFinder) * @since 1.0 */ public ResourceFinder getResourceFinder() { return resourcefinder; } /** * Sets the resource finder that can be used during the * <code>initialize()</code> method. * * @param resourceFinder A <code>ResourceFinder</code> instance containing * the resource finder that is used during the initialization of the * repository and its participants. * @see #getResourceFinder() * @since 1.0 */ public void setResourceFinder(ResourceFinder resourceFinder) { resourcefinder = resourceFinder; } /** * Starts the initialization. * * @since 1.0 */ public final void run() { // Only initialize once. if (!isFinished()) { try { initialize(); } catch (Throwable e) { initializationError = true; getThread().getThreadGroup().uncaughtException(thread, e); } finally { // Obtain a lock on the synchronization monitor of this particular // participant to make it possible to notify all waiting threads. synchronized (initThreadMonitor) { initializationFinished = true; initThreadMonitor.notifyAll(); repository.fireInitActionPerformed(this); } } } } /** * Checks if the initialization of this participant is finished. * * @return <code>true</code> if the initialization is finished; or * <p><code>false</code> if the initialization is in progress * @since 1.0 */ public final boolean isFinished() { return initializationFinished; } /** * Checks if the initialization of this participant threw an error. * * @return <code>true</code> if the initialization threw an error; or * <p><code>false</code> if the initialization was successful * @since 1.0 */ public boolean hadInitializationError() { synchronized (initThreadMonitor) { return initializationError; } } /** * Makes the calling thread wait until the initialization of this * participant has finished. * * @since 1.0 */ public final void waitUntilFinished() { // Obtain a lock on the synchronization monitor of this particular // participant to make it possible to wait for notifications on this // monitor. synchronized (initThreadMonitor) { // Only make the calling thread wait if the initialization is still // busy. while (!isFinished()) { try { initThreadMonitor.wait(); } catch (InterruptedException ignored) { } } } } /** * Returns a message that is supposed to describe the initialization of * this participant. If no message has been set with <code>setInitializationMessage(String * message)</code>, a default message is generated. * * @return A <code>String</code> containing the message that describes the * initialization of this participant. * @see #setInitializationMessage(String) * @since 1.0 */ public String getInitializationMessage() { if (null == initializationMessage) { return "Initializing '" + this.getClass().getName() + "' ..."; } else { return initializationMessage; } } /** * Overrides the default message that describes the initialization of this * participant. * * @param message A <code>String</code> containing the message. * @see #getInitializationMessage() * @since 1.0 */ public void setInitializationMessage(String message) { initializationMessage = message; } /** * Returns a message that is supposed to describe the cleanup of this * participant. If no message has been set with <code>setCleanupMessage(String * message)</code>, a default message is generated. * * @return A <code>String</code> containing the message that describes the * cleanup of this participant. * @see #setCleanupMessage(String) * @since 1.0 */ public String getCleanupMessage() { if (null == cleanupMessage) { return "Cleaning up '" + this.getClass().getName() + "' ..."; } else { return cleanupMessage; } } /** * Overrides the default message that describes the cleanup of this * participant. * * @param message A <code>String</code> containing the message. * @see #getCleanupMessage() * @since 1.0 */ public void setCleanupMessage(String message) { cleanupMessage = message; } /** * Returns the default object for this participant. * <p>If the initialization of the participant hasn't finished yet, the * thread that executes this method will be suspended and woken up when * the initialization finishes. * * @return <code>null</code> if no default object exists; or * <p>an <code>Object</code> instance containing the default object * @see #getObject(Object) * @see #_getObject(Object) * @since 1.0 */ public final Object getObject() { // if the participant is finished, return the object directly if (isFinished()) { return _getObject(); } return getObjectAndWait(); } private Object getObjectAndWait() { // Obtain a lock on the synchronization monitor of this particular // participant to make it possible to wait for notifications on this // monitor. synchronized (initThreadMonitor) { // If the initialization hasn't finished, suspend the executing // thread. while (!isFinished()) { try { initThreadMonitor.wait(); } catch (InterruptedException e) { // do nothing } } return _getObject(); } } protected Object _getObject() { return getObject(null); } /** * Retrieves the object from the participant that corresponds to a * particular key. * <p>If the initialization of the participant hasn't finished yet, the * thread that executes this method will be suspended and woken up when * the initialization finishes. * * @param key An <code>Object</code> instance that used as the key to * obtain a corresponding object from the participant with. * @return <code>null</code> if no object could be found that corresponds * to the provided key; or * <p>the requested <code>Object</code> instance * @see #getObject() * @see #_getObject(Object) * @since 1.0 */ public final Object getObject(Object key) { // if the participant is finished, return the object directly if (isFinished()) { return _getObject(key); } return getObjectAndWait(key); } private Object getObjectAndWait(Object key) { // Obtain a lock on the synchronization monitor of this particular // participant to make it possible to wait for notifications on this // monitor. synchronized (initThreadMonitor) { // If the initialization hasn't finished, suspend the executing // thread. while (!isFinished()) { try { initThreadMonitor.wait(); } catch (InterruptedException e) { // do nothing } } return _getObject(key); } } Thread getThread() { return thread; } void setThread(Thread thread) { this.thread = thread; if (threadName != null) { this.thread.setName(threadName); } } }