com.lucidtechnics.blackboard.Blackboard.java Source code

Java tutorial

Introduction

Here is the source code for com.lucidtechnics.blackboard.Blackboard.java

Source

/*
* Copyright 2002-2006 Bediako George.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.lucidtechnics.blackboard;

import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.HashSet;
import java.util.Date;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;

import com.lucidtechnics.blackboard.config.WorkspaceConfiguration;
import com.lucidtechnics.blackboard.config.EventConfiguration;

import com.lucidtechnics.blackboard.util.error.ErrorManager;
import com.lucidtechnics.blackboard.util.PropertyUtil;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Blackboard {
    private static Log logger = LogFactory.getLog(Blackboard.class);

    private ErrorManager errorManager;
    private BlackboardActor blackboardActor;
    private int maxBlackboardThread = 1;
    private int maxScheduledBlackboardThread = 1;
    private int maxWorkspaceThread;
    private int maxPersistenceThread;
    private int maxWorkspace;
    private int activeWorkspaceCount;
    private ThreadPoolExecutor blackboardExecutor;
    private ScheduledThreadPoolExecutor scheduledBlackboardExecutor;
    private ThreadPoolExecutor workspaceExecutor;
    private ThreadPoolExecutor persistenceExecutor;
    private Map<String, WorkspaceConfiguration> eventToWorkspaceMap;
    private Persister persister;
    private boolean timePlans;
    private String appsHome;
    private Map<Integer, ThreadPoolExecutor> workspaceExecutorMap;
    private int currentWorkspaceExecutor = 0;

    private ErrorManager getErrorManager() {
        return errorManager;
    }

    private BlackboardActor getBlackboardActor() {
        return blackboardActor;
    }

    private int getMaxBlackboardThread() {
        return maxBlackboardThread;
    }

    private int getMaxScheduledBlackboardThread() {
        return maxScheduledBlackboardThread;
    }

    private int getMaxWorkspaceThread() {
        return maxWorkspaceThread;
    }

    private int getMaxPersistenceThread() {
        return maxPersistenceThread;
    }

    private int getMaxWorkspace() {
        return maxWorkspace;
    }

    private int getActiveWorkspaceCount() {
        return activeWorkspaceCount;
    }

    private ThreadPoolExecutor getBlackboardExecutor() {
        return blackboardExecutor;
    }

    private ScheduledThreadPoolExecutor getScheduledBlackboardExecutor() {
        return scheduledBlackboardExecutor;
    }

    private ThreadPoolExecutor getPersistenceExecutor() {
        return persistenceExecutor;
    }

    private Map<String, WorkspaceConfiguration> getEventToWorkspaceMap() {
        return eventToWorkspaceMap;
    }

    private Persister getPersister() {
        return persister;
    }

    private boolean getTimePlans() {
        return timePlans;
    }

    private String getAppsHome() {
        return appsHome;
    }

    private Map<Integer, ThreadPoolExecutor> getWorkspaceExecutorMap() {
        return workspaceExecutorMap;
    }

    private void setErrorManager(ErrorManager _errorManager) {
        errorManager = _errorManager;
    }

    private void setBlackboardActor(BlackboardActor _blackboardActor) {
        blackboardActor = _blackboardActor;
    }

    private void setMaxBlackboardThread(int _maxBlackboardThread) {
        maxBlackboardThread = _maxBlackboardThread;
    }

    private void setMaxScheduledBlackboardThread(int _maxScheduledBlackboardThread) {
        maxScheduledBlackboardThread = _maxScheduledBlackboardThread;
    }

    private void setMaxWorkspaceThread(int _maxWorkspaceThread) {
        maxWorkspaceThread = _maxWorkspaceThread;
    }

    private void setMaxPersistenceThread(int _maxPersistenceThread) {
        maxPersistenceThread = _maxPersistenceThread;
    }

    private void setMaxWorkspace(int _maxWorkspace) {
        maxWorkspace = _maxWorkspace;
    }

    private void setActiveWorkspaceCount(int _activeWorkspaceCount) {
        activeWorkspaceCount = _activeWorkspaceCount;
    }

    private void setBlackboardExecutor(ThreadPoolExecutor _blackboardExecutor) {
        blackboardExecutor = _blackboardExecutor;
    }

    private void setScheduledBlackboardExecutor(ScheduledThreadPoolExecutor _scheduledBlackboardExecutor) {
        scheduledBlackboardExecutor = _scheduledBlackboardExecutor;
    }

    private void setPersistenceExecutor(ThreadPoolExecutor _persistenceExecutor) {
        persistenceExecutor = _persistenceExecutor;
    }

    private void setEventToWorkspaceMap(Map<String, WorkspaceConfiguration> _eventToWorkspaceMap) {
        eventToWorkspaceMap = _eventToWorkspaceMap;
    }

    private void setPersister(Persister _persister) {
        persister = _persister;
    }

    private void setTimePlans(boolean _timePlans) {
        timePlans = _timePlans;
    }

    private void setAppsHome(String _appsHome) {
        appsHome = _appsHome;
    }

    private void setWorkspaceExecutorMap(Map<Integer, ThreadPoolExecutor> _workspaceExecutorMap) {
        workspaceExecutorMap = _workspaceExecutorMap;
    }

    public Blackboard(BlackboardConfiguration _blackboardConfiguration) {
        configureBlackboard(_blackboardConfiguration);

        setBlackboardActor(new BlackboardActor("Blackboard"));
        setEventToWorkspaceMap(new HashMap());
        setWorkspaceExecutorMap(new HashMap<Integer, ThreadPoolExecutor>());

        init();
    }

    public Blackboard() {
        this(new BlackboardConfiguration());
    }

    private void configureBlackboard(BlackboardConfiguration _blackboardConfiguration) {
        java.io.File blackboardConfigurationFile = new java.io.File(
                "." + java.io.File.separator + "blackboard.configuration.js");

        if (blackboardConfigurationFile.exists() == true) {
            JavaScriptConfigurator configurator = new JavaScriptConfigurator();
            String configuratorPath = blackboardConfigurationFile.getAbsolutePath();
            configurator.execute(_blackboardConfiguration, configuratorPath);
        }

        this.setErrorManager(_blackboardConfiguration.getErrorManager());
        this.setMaxBlackboardThread(_blackboardConfiguration.getMaxBlackboardThread());
        this.setMaxScheduledBlackboardThread(_blackboardConfiguration.getMaxScheduledBlackboardThread());
        this.setMaxWorkspaceThread(_blackboardConfiguration.getMaxWorkspaceThread());
        this.setMaxPersistenceThread(_blackboardConfiguration.getMaxPersistenceThread());
        this.setMaxWorkspace(_blackboardConfiguration.getMaxWorkspace());
        this.setPersister(_blackboardConfiguration.getPersister());
        this.setTimePlans(_blackboardConfiguration.getTimePlans());
        this.setAppsHome(_blackboardConfiguration.getAppsHome());
    }

    private void init() {
        java.io.File file = new java.io.File(getAppsHome());

        if (file.exists() == false) {
            file.mkdirs();
        }

        java.io.File appsDirectory = new java.io.File(getAppsHome());

        if (appsDirectory.isDirectory() != true) {
            throw new RuntimeException(
                    "Directory: " + getAppsHome() + " as set in blackboard.apps.home is not a directory");
        }

        java.io.File[] directoryFiles = appsDirectory.listFiles();

        for (int i = 0; i < directoryFiles.length; i++) {
            if (directoryFiles[i].isDirectory() == true) {
                String appName = directoryFiles[i].getName();

                if (logger.isInfoEnabled() == true) {
                    logger.info("Configuring app: " + appName);
                }

                java.io.File[] workspaceDirectoryFiles = directoryFiles[i].listFiles();

                for (int j = 0; j < workspaceDirectoryFiles.length; j++) {
                    if (workspaceDirectoryFiles[j].isDirectory() == true) {
                        String workspaceName = workspaceDirectoryFiles[j].getName();

                        if (logger.isInfoEnabled() == true) {
                            logger.info("Processing workspace: " + workspaceName);
                        }

                        java.io.File[] eventDirectoryFiles = workspaceDirectoryFiles[j].listFiles();

                        WorkspaceConfiguration workspaceConfiguration = configureWorkspace(appName, workspaceName,
                                workspaceDirectoryFiles[j]);

                        for (int k = 0; k < eventDirectoryFiles.length; k++) {
                            if (eventDirectoryFiles[k].isDirectory() == true) {
                                processEventPlans(appName, workspaceName, workspaceConfiguration,
                                        eventDirectoryFiles[k]);
                            }
                        }
                    }
                }
            }
        }

        if (logger.isInfoEnabled() == true) {
            logger.info("Loaded event configurations: " + getEventToWorkspaceMap());
        }

        setBlackboardExecutor(new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingQueue()));

        setScheduledBlackboardExecutor(new ScheduledThreadPoolExecutor(getMaxScheduledBlackboardThread()));

        for (int i = 0; i <= getMaxWorkspaceThread(); i++) {
            getWorkspaceExecutorMap().put(i,
                    new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingQueue()));
        }

        setPersistenceExecutor(new ThreadPoolExecutor(getMaxPersistenceThread(), getMaxPersistenceThread(), 100,
                TimeUnit.SECONDS, new LinkedBlockingQueue()));

        if (logger.isInfoEnabled() == true) {
            logger.info("Blackboard Workspace Server Initialization Inception.");
            logger.info("Apache 2.0 Open Source License.");
            logger.info("Copyright Owner - Lucid Technics, LLC.");
            logger.info("Authors - Bediako Ntodi George and David Yuctan Hodge.");
            logger.info("Initialization was successful.");
        }

        org.apache.jcs.JCS.setConfigFilename("/blackboard.ccf");
    }

    protected void executePlans(final TargetSpace _targetSpace, final java.util.Collection<Plan> _planList) {
        WorkspaceExecutionContext workspaceExecutionContext = get(_targetSpace.getWorkspaceIdentifier());
        ThreadPoolExecutor workspaceExecutor = getWorkspaceExecutorMap()
                .get(workspaceExecutionContext.getWorkspaceExecutorName());

        workspaceExecutor.execute(new Runnable() {
            public void run() {
                long startWorkspaceRun = 0l;
                long endWorkspaceRun = 0l;

                if (getTimePlans() == true) {
                    startWorkspaceRun = System.currentTimeMillis();
                }

                WorkspaceContext workspaceContext = null;
                Plan plan = null;
                Map planToChangeInfoCountMap = new HashMap();
                boolean notifyPlans = false;
                Set activePlanSet = new HashSet();

                try {
                    _targetSpace.setExecuting();

                    for (Plan executingPlan : _planList) {
                        plan = executingPlan;

                        if (_targetSpace.isTerminated() == false && _targetSpace.isActive(plan) == true) {
                            try {
                                activePlanSet.add(plan);

                                if (_targetSpace.isFinished(plan) == false) {
                                    if (logger.isDebugEnabled() == true) {
                                        logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                                + " plan: " + plan.getName() + " is about to be executed.");
                                    }

                                    _targetSpace.setExecuting(plan);

                                    try {
                                        if (getTimePlans() == true) {
                                            endWorkspaceRun = System.currentTimeMillis();

                                            if (logger.isInfoEnabled() == true) {
                                                logger.info("Processing target space time: "
                                                        + (endWorkspaceRun - startWorkspaceRun));
                                            }
                                        }

                                        workspaceContext = new WorkspaceContext(_targetSpace, plan);

                                        long startTime = 0l;
                                        long endTime = 0l;
                                        long totalTime = 0l;

                                        if (getTimePlans() == true) {
                                            startTime = System.currentTimeMillis();
                                        }

                                        boolean exceptionThrown = false;
                                        //Execute the plan
                                        try {
                                            _targetSpace.setPlanState(plan, plan.execute(workspaceContext));
                                        } catch (Throwable t) {
                                            //If a plan self destructs
                                            //the whole target space is
                                            //considered compromised.
                                            //The target space is
                                            //retired and the target
                                            //clean up.  This is to
                                            //prevent inadvertant
                                            //memory leaks. Plan
                                            //operators should strive
                                            //to not have their plans
                                            //throw unhandled exceptions.
                                            exceptionThrown = true;

                                            if (_targetSpace.getTerminateOnError() == true) {
                                                getErrorManager().logException(t, logger);
                                                _targetSpace.setTerminated();
                                            } else {
                                                getErrorManager().warnException(t, logger);
                                            }
                                        }

                                        if (getTimePlans() == true) {
                                            endTime = System.currentTimeMillis();
                                            totalTime = endTime - startTime;

                                            if (logger.isInfoEnabled() == true) {
                                                logger.info(
                                                        "Plan: " + plan.getName() + " executed in: " + totalTime);
                                            }
                                        }

                                        if (_targetSpace.isFinished(plan) == true) {
                                            if (logger.isDebugEnabled() == true) {
                                                logger.debug("For workspace: "
                                                        + _targetSpace.getWorkspaceIdentifier() + " plan: "
                                                        + plan.getName() + " ran and is now finished.");
                                            }
                                        }

                                        _targetSpace.setLastActiveTime(System.currentTimeMillis());
                                    } finally {
                                        workspaceContext.expire();

                                        //Keep track of the state of
                                        //the workspace when this plan
                                        //was finished. Later on we
                                        //will check to see if the
                                        //workspace had been changed by
                                        //other plans since this plan
                                        //was last run.
                                    }

                                    planToChangeInfoCountMap.put(plan,
                                            new Integer(_targetSpace.getChangeInfoCount()));
                                } else {
                                    if (logger.isDebugEnabled() == true) {
                                        logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                                + " plan: " + plan.getName() + " is finished.");
                                    }

                                    activePlanSet.remove(plan);
                                }

                                if (logger.isDebugEnabled() == true) {
                                    logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                            + " plan: " + plan.getName() + " executed successfully.");
                                }
                            } catch (Throwable t) {
                                _targetSpace.setErrored(plan, t);
                                activePlanSet.remove(plan);
                                logger.error("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                        + " encountered exception while trying to execute plan: " + plan);
                                getErrorManager().logException(t, logger);
                            } finally {
                                _targetSpace.setExecuted(plan);

                                if (_targetSpace.isFinished(plan) == true && activePlanSet.contains(plan) == true) {
                                    activePlanSet.remove(plan);
                                }
                            }
                        }
                    }

                    for (Iterator activePlans = activePlanSet.iterator(); activePlans.hasNext() == true;) {
                        plan = (Plan) activePlans.next();

                        if (logger.isDebugEnabled() == true) {
                            logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier() + " plan: "
                                    + plan.getName() + " is an active plan.");
                        }

                        if (_targetSpace.isFinished(plan) == true) {
                            if (logger.isDebugEnabled() == true) {
                                logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier() + " plan: "
                                        + plan.getName() + " is now a finished plan.");
                            }

                            activePlans.remove();
                        } else {
                            if (logger.isDebugEnabled() == true) {
                                logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier() + " plan: "
                                        + plan.getName() + " is still an active plan.");
                            }

                            int planChangeCount = 0;
                            Integer planChangeCountInteger = (Integer) planToChangeInfoCountMap.get(plan);
                            planChangeCount = planChangeCountInteger.intValue();

                            if (_targetSpace.getChangeInfoCount() > planChangeCount) {
                                //Plan is interested in the
                                //workspace and there have been
                                //changes since it was last
                                //run.

                                if (logger.isDebugEnabled() == true) {
                                    logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                            + " notifying plans for plan: " + plan.getName());
                                }

                                notifyPlans = true;
                            } else {
                                if (logger.isDebugEnabled() == true) {
                                    logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                            + " NOT notifying plans for plan: " + plan.getName());
                                }
                            }
                        }

                        if (logger.isDebugEnabled() == true) {
                            logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                    + " setting workspace as executed.");
                        }
                    }

                    if (activePlanSet.isEmpty() == true) {
                        _targetSpace.setCompleted();
                    }
                } finally {
                    if (getTimePlans() == true) {
                        endWorkspaceRun = System.currentTimeMillis();

                        if (logger.isInfoEnabled() == true) {
                            logger.info("Processing target space time at beginning of finally: "
                                    + (endWorkspaceRun - startWorkspaceRun));
                        }
                    }

                    if (((_targetSpace.isCompleted() == true) || _targetSpace.isTerminated() == true)
                            && _targetSpace.isPersisted() == false) {
                        remove(_targetSpace.getWorkspaceIdentifier());
                        retireTargetSpace(_targetSpace);
                    } else if (notifyPlans == true) {
                        if (logger.isDebugEnabled() == true) {
                            logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                    + " execute plan will notify plans.");
                        }

                        _targetSpace.setActive();
                        _targetSpace.notifyPlans();
                    } else {
                        _targetSpace.setActive();

                        if (logger.isDebugEnabled() == true) {
                            logger.debug("For workspace: " + _targetSpace.getWorkspaceIdentifier()
                                    + " execute plan will NOT notify plans");
                        }
                    }

                    if (getTimePlans() == true) {
                        endWorkspaceRun = System.currentTimeMillis();

                        if (logger.isInfoEnabled() == true) {
                            logger.info("Processing target space time: " + (endWorkspaceRun - startWorkspaceRun));
                        }
                    }
                }
            }
        });
    }

    /**
     * Places a target directly on the Blackboard.  The target must
     * have a {@link Event} annotation or the Blackboard will
     * eventually throw an Exception.
     *
     * Developer's can use this method to effectively create a whole
     * new workspace.  It the appName, workspaceName, name and the
     * value of the workspace identifier are the same as that of the
     * event that caused this workspace creation, if this workspace is
     * still active at the time of _object's arrival, the _target will
     * appear on this workspace.  If any of those values are different
     * the _target will appear on another workspace.
     *
     * @param _target the name of the target to be placed on the workspace.
     * @see Event
     * 
     */

    public void placeOnBlackboard(Target _target) {
        if (logger.isDebugEnabled() == true) {
            logger.debug("Placing on blackboard the target: " + _target);
        }

        placeOnBlackboard(_target.getBlackboardObject());
    }

    /**
     * Places an object directly on the Blackboard.  The object must
     * have a {@link Event} annotation or the Blackboard will
     * eventually throw an Exception.
     *
     * Developer's can use this method to effectively create a whole
     * new workspace.  It the appName, workspaceName, name and the
     * value of the workspace identifier are the same as that of the
     * event that caused this workspace creation, if this workspace is
     * still active at the time of _object's arrival, the _object will
     * appear on this workspace.  If any of those values are different
     * the _object will appear on another workspace.
     *
     * @param _object the name of the target to be placed on the workspace.
     * @see Event
     * 
     */

    public void placeOnBlackboard(final Object _event) {
        if (logger.isDebugEnabled() == true) {
            logger.debug("Placing on blackboard the event: " + _event);
        }

        getBlackboardExecutor().execute(new Runnable() {
            public void run() {
                try {
                    if (logger.isDebugEnabled() == true) {
                        logger.debug("o");
                    }

                    if (_event instanceof WorkspaceExecutionContext) {
                        Object workspaceIdentifier = ((WorkspaceExecutionContext) _event).getWorkspaceIdentifier();

                        if (logger.isTraceEnabled() == true) {
                            logger.trace("Cleaning up context for workspace identified by: " + workspaceIdentifier);
                        }

                        remove(workspaceIdentifier);
                    } else {
                        add(_event);

                    }
                } catch (Throwable t) {
                    logger.error("Caught exception: " + t.toString() + " while trying to add event: " + _event
                            + " to the blackboard.", t);
                }
            }
        });
    }

    /**
     * Places a target directly on the Blackboard in the same
     * manner as placeOnBlackboard(Target _target) except that it
     * delays the arrival of that _target until _delay milliseconds has
     * passed.  The target must have a {@link Event} annotation or the Blackboard
     * will eventually throw an Exception.
     * 
     * @param _target the name of the target to be placed on the workspace.
     * @param _delay the time in milliseconds that must pass before
     * event appears on workspace.
     */

    public void schedulePlaceOnBlackboard(final Target _target, long _delay) {
        schedulePlaceOnBlackboard(_target.getBlackboardObject(), _delay);
    }

    /**
     * Places an object on directly on the Blackboard in the same
     * manner as placeOnBlackboard(Object _object) except that it
     * delays the arrival of that _object until _delay milliseconds has
     * passed.  The object must have a {@link Event} annotation or the Blackboard
     * will eventually throw an Exception.
     * 
     * @param _object the name of the target to be placed on the workspace.
     * @param _delay the time in milliseconds that must pass before
     * event appears on workspace.
     */

    public void schedulePlaceOnBlackboard(final Object _event, long _delay) {
        getScheduledBlackboardExecutor().schedule(new Runnable() {
            public void run() {
                try {
                    placeOnBlackboard(_event);
                } catch (Throwable t) {
                    logger.error("Caught exception: " + t.toString() + " while trying to add event: " + _event
                            + " to the blackboard.", t);
                }
            }
        }, _delay, TimeUnit.MILLISECONDS);
    }

    private List<String> determineEventNames(Object _event) {
        List<String> eventNameList = new ArrayList<String>();

        Class eventClass = _event.getClass();

        if (eventClass.isAnnotationPresent(Event.class) == true) {
            Event event = (Event) eventClass.getAnnotation(Event.class);

            if (event.name() != null) {
                String[] nameArray = event.name().split(",");

                for (int i = 0; i < nameArray.length; i++) {
                    eventNameList.add(createFullEventName(event.appName(), event.workspaceName(), nameArray[i]));
                }
            }
        }

        return eventNameList;
    }

    private String determineWorkspaceIdentifierName(Object _event) {
        Class eventClass = _event.getClass();
        String propertyName = null;

        if (eventClass.isAnnotationPresent(Event.class) == true) {
            Event event = (Event) eventClass.getAnnotation(Event.class);

            if (event.workspaceIdentifier() != null) {
                propertyName = event.workspaceIdentifier();
            }
        }

        return propertyName;
    }

    private void add(Object _event) {
        try {
            if (logger.isDebugEnabled() == true) {
                logger.debug("Object identified by class: " + _event.getClass() + " was added on the blackboard.");
            }

            boolean foundEventConfiguration = false;

            List<String> eventNameList = determineEventNames(_event);

            if (eventNameList.isEmpty() == true) {
                throw new RuntimeException("Unknown event: " + _event);
            }

            for (String eventName : eventNameList) {
                if (logger.isDebugEnabled() == true) {
                    logger.debug("Object of class: " + _event.getClass() + " is event " + eventName + " .");
                }

                WorkspaceConfiguration workspaceConfiguration = getEventToWorkspaceMap().get(eventName);

                if (workspaceConfiguration == null) {
                    throw new RuntimeException("Unable to find the workspace configuration for eventName: "
                            + eventName + ". Please check your com.lucidtechnics.blackboard.Event annotation "
                            + " as it is defined in the class: " + _event.getClass()
                            + " and make sure the Event annotation attributes appName, workspaceName, and name "
                            + " maps to the path under blackboard's \"apps\" directory <appName>/<workspaceName>/<name>");
                }

                Object workspaceIdentifier = PropertyUtils.getProperty(_event,
                        determineWorkspaceIdentifierName(_event));
                addToTargetSpace(workspaceConfiguration, workspaceIdentifier, eventName, _event);
                foundEventConfiguration = true;
            }

            if (foundEventConfiguration == false) {
                logger.error("Object identified by class: " + _event.getClass()
                        + " was not processed as it is not defined as an event.");
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    protected void addToTargetSpace(WorkspaceConfiguration _workspaceConfiguration, Object _workspaceIdentifier,
            String _eventName, Object _event) throws Exception {
        TargetSpace targetSpace = retrieveTargetSpace(_workspaceIdentifier, _workspaceConfiguration);

        if (logger.isDebugEnabled() == true) {
            logger.debug("For workspace: " + _workspaceIdentifier + " putting event " + _eventName);
        }

        targetSpace.put(_eventName, _event, getBlackboardActor(), null);
    }

    private TargetSpace retrieveTargetSpace(Object _workspaceIdentifier,
            WorkspaceConfiguration _workspaceConfiguration) {
        WorkspaceExecutionContext workspaceExecutionContext = get(_workspaceIdentifier);
        TargetSpace targetSpace = workspaceExecutionContext.getTargetSpace();

        if (targetSpace == null) {
            targetSpace = new TargetSpaceImpl(_workspaceIdentifier);
            targetSpace.addPlans(_workspaceConfiguration.getPlanSet());
            workspaceExecutionContext.setTargetSpace(targetSpace);
        }

        if (targetSpace.getBlackboard() == null) {
            targetSpace.initialize(this);

            targetSpace.setAppName(_workspaceConfiguration.getAppName());
            targetSpace.setName(_workspaceConfiguration.getWorkspaceName());
            targetSpace.setDoNotPersistSet(_workspaceConfiguration.getDoNotPersistSet());
            targetSpace.setPersistChangeInfoHistory(_workspaceConfiguration.getPersistChangeInfoHistory());
            targetSpace.setTerminateOnError(_workspaceConfiguration.getTerminateOnError());
            targetSpace.setWorkspaceConfiguration(_workspaceConfiguration);

            long currentTimeMillis = System.currentTimeMillis();
            targetSpace.setLastActiveTime(currentTimeMillis);
        }

        return targetSpace;
    }

    private void incrementActiveWorkspaceCount() {
        setActiveWorkspaceCount(getActiveWorkspaceCount() + 1);

        if (logger.isDebugEnabled() == true) {
            logger.debug(getActiveWorkspaceCount());
        }
    }

    private void decrementActiveWorkspaceCount() {
        setActiveWorkspaceCount(getActiveWorkspaceCount() - 1);

        if (logger.isDebugEnabled() == true) {
            logger.debug(getActiveWorkspaceCount());
        }
    }

    protected void retireTargetSpace(final TargetSpace _targetSpace) {
        getPersistenceExecutor().execute(new Runnable() {
            public void run() {
                _targetSpace.setRetired();
                _targetSpace.setPersisted();
                _targetSpace.setRetireDate(new Date());

                TargetSpace targetSpace = _targetSpace.prepareForRetirement();

                persistTargetSpace(targetSpace);

                //Placing the workspaceExcutionContext on the
                //blackboard, signals the blackboard execution
                //thread that this workspace execution is finished
                //and has been retired. The workspace is then removed
                //from the cache.  This avoids the need to establish an
                //additional lock for the critical cache region. 
                WorkspaceExecutionContext workSpaceExecutionContext = get(_targetSpace.getWorkspaceIdentifier());
                placeOnBlackboard(workSpaceExecutionContext);

                if (logger.isDebugEnabled() == true) {
                    logger.debug("Target space identified by name: " + _targetSpace.getName() + " and id: "
                            + _targetSpace.getWorkspaceIdentifier() + " is retired.");
                }
            }
        });
    }

    private void persistTargetSpace(TargetSpace _targetSpace) {
        TargetSpace targetSpace = _targetSpace.prepareForPersistence();
        getPersister().put(_targetSpace);
    }

    protected void processEventPlans(String _appName, String _workspaceName,
            WorkspaceConfiguration _workspaceConfiguration, java.io.File _eventPlanDirectory) {
        if (logger.isDebugEnabled() == true) {
            logger.debug("Getting plans from event directory: " + _eventPlanDirectory.getName());
        }

        //execute workspace configuration returns workspace
        //configuration for this workspace.

        java.io.File[] planArray = _eventPlanDirectory.listFiles();

        for (int i = 0; i < planArray.length; i++) {
            if (planArray[i].isDirectory() == false && planArray[i].getName().endsWith(".js") == true) {
                if (logger.isInfoEnabled() == true) {
                    logger.info("Loading plan: " + planArray[i].getName());
                }

                _workspaceConfiguration.getPlanSet()
                        .add(new JavaScriptPlan(planArray[i].getName(), planArray[i].getAbsolutePath()));
            } else if (planArray[i].isDirectory() == false) {
                String[] tokenArray = planArray[i].getName().split("\\.");
                String extension = tokenArray[tokenArray.length - 1];

                if (Jsr223Plan.hasScriptingEngine(extension) == true) {
                    if (logger.isInfoEnabled() == true) {
                        logger.info("Loading plan: " + planArray[i].getName());
                    }

                    _workspaceConfiguration.getPlanSet()
                            .add(new Jsr223Plan(planArray[i].getName(), planArray[i].getAbsolutePath(), extension));
                } else {
                    if (logger.isWarnEnabled() == true) {
                        logger.warn("Not scriptable: " + planArray[i].getName());
                    }
                }
            }
        }

        String eventName = extractEventName(_eventPlanDirectory.getName());

        getEventToWorkspaceMap().put(createFullEventName(_appName, _workspaceName, eventName),
                _workspaceConfiguration);
    }

    protected WorkspaceConfiguration configureWorkspace(String _appName, String _workspaceName,
            java.io.File _eventPlanDirectory) {
        WorkspaceConfiguration workspaceConfiguration = new WorkspaceConfiguration(_appName, _workspaceName);

        java.io.File[] configurationFileArray = _eventPlanDirectory.listFiles();
        Configurator configurator = null;
        String configuratorPath = null;

        for (int i = 0; i < configurationFileArray.length; i++) {
            if (configurationFileArray[i].isDirectory() == false
                    && configurationFileArray[i].getName().endsWith("workspaceConfiguration.js") == true) {
                if (logger.isInfoEnabled() == true) {
                    logger.info("Loading configuration: " + configurationFileArray[i].getName());
                }

                configurator = new JavaScriptConfigurator();
                configuratorPath = configurationFileArray[i].getAbsolutePath();
            } else if (configurationFileArray[i].isDirectory() == false
                    && configurationFileArray[i].getName().contains("workspaceConfiguration") == true) {
                if (logger.isInfoEnabled() == true) {
                    logger.info("Loading configuration: " + configurationFileArray[i].getName());
                }

                String[] tokenArray = configurationFileArray[i].getName().split("\\.");
                String extension = tokenArray[tokenArray.length - 1];

                configurator = new Jsr223Configurator(extension);
                configuratorPath = configurationFileArray[i].getAbsolutePath();
            }
        }

        if (configuratorPath != null) {
            if (logger.isInfoEnabled() == true) {
                logger.info("Executing this configuration: " + configuratorPath);
            }

            configurator.execute(workspaceConfiguration, configuratorPath);
        } else {
            if (logger.isInfoEnabled() == true) {
                logger.info(
                        "No workspaceConfiguration.js/rb file found for directory: " + _eventPlanDirectory + ".");
            }
        }

        return workspaceConfiguration;
    }

    protected String createFullEventName(String _appName, String _workspaceName, String _eventName) {
        if (logger.isDebugEnabled() == true) {
            logger.debug("Creating event name from app name: " + _appName + " and workspace name: " + _workspaceName
                    + " and event name: " + _eventName);
        }

        return _appName + "." + _workspaceName + "." + _eventName;
    }

    protected String extractEventName(String _pathName) {
        String pathName = _pathName.replaceAll(java.io.File.pathSeparator + "$", "");
        String[] tokenArray = pathName.split(java.io.File.pathSeparator);

        return tokenArray[tokenArray.length - 1];
    }

    private void put(Object _workspaceIdentifier, WorkspaceExecutionContext _workspaceExecutionContext) {
        try {
            org.apache.jcs.JCS jcs = org.apache.jcs.JCS.getInstance("blackboard");
            jcs.put(_workspaceIdentifier, _workspaceExecutionContext);
            incrementActiveWorkspaceCount();
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    private WorkspaceExecutionContext get(Object _workspaceIdentifier) {
        WorkspaceExecutionContext workspaceExecutionContext = null;

        try {
            org.apache.jcs.JCS jcs = org.apache.jcs.JCS.getInstance("blackboard");
            workspaceExecutionContext = (WorkspaceExecutionContext) jcs.get(_workspaceIdentifier);

            if (workspaceExecutionContext == null) {
                if (currentWorkspaceExecutor >= getMaxWorkspaceThread()) {
                    currentWorkspaceExecutor = 0;
                }

                workspaceExecutionContext = new WorkspaceExecutionContext(_workspaceIdentifier,
                        currentWorkspaceExecutor);

                currentWorkspaceExecutor++;

                put(_workspaceIdentifier, workspaceExecutionContext);
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }

        return workspaceExecutionContext;
    }

    private boolean contains(Object _workspaceIdentifier) {
        try {
            org.apache.jcs.JCS jcs = org.apache.jcs.JCS.getInstance("blackboard");
            return (jcs.get(_workspaceIdentifier) != null);
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    protected void remove(Object _workspaceIdentifier) {
        try {
            org.apache.jcs.JCS jcs = org.apache.jcs.JCS.getInstance("blackboard");
            jcs.remove(_workspaceIdentifier);
            decrementActiveWorkspaceCount();

            if (logger.isTraceEnabled() == true) {
                logger.trace("Active workspace count is now: " + getActiveWorkspaceCount());
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }
}