appserver-io/concurrency

View on GitHub
src/ExecutorService/Core.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

namespace AppserverIo\Concurrency\ExecutorService;

use AppserverIo\Concurrency\ExecutorService;
use AppserverIo\Concurrency\AppserverIo\Concurrency;

/**
 * Provides the core functionality for executor service instances
 *
 * @author Johann Zelger <jz@appserver.io>
 */
class Core
{
    /**
     * Defines the entity key prefix for internal key storage
     *
     * @var string
     */
    const ENTITY_KEY_PREFIX = '__entity/';

    /**
     * Defines the execution method types
     *
     * @var string
     */
    const EX_METHOD_DEFAULT = '__execute';
    const EX_METHOD_DIRECT = '__executeDirect';
    const EX_METHOD_ASYNCHRONOUS = '__executeAsynchronous';
    const EX_METHOD_SYNCHRONIZED = '__executeSynchronized';

    /**
     * Defines internal commands for entity management
     *
     * @var string
     */
    const EX_CMD_ENTITY_RETURN = '__return';
    const EX_CMD_ENTITY_INVOKE = '__invoke';
    const EX_CMD_ENTITY_RESET = '__reset';

    /**
     * Defines internal commands for executor service handling
     *
     * @var string
     */
    const EX_CMD_INIT = '__init';
    const EX_CMD_SHUTDOWN = '__shutdown';
    const EX_CMD_CALLBACK = '__callback';

    /**
     * Initialises the global storage.
     * This fuction should be called on global scope.
     *
     * @param string $autoloader The path to the autoloaders class to require
     * @param string $mainEntity The entity to use for the main instance
     *
     * @static
     * @return void
     */
    public static function init($autoloader = null, $mainEntity = '\stdClass')
    {
        $globalVarName = self::getGlobalVarName();
        global $$globalVarName;
        if (is_null($$globalVarName)) {
            return $$globalVarName = new ExecutorService($mainEntity, $autoloader);
        }
    }

    /**
     * Shutdown the executor service and all its registered entities
     *
     * @param string $entityTypeOrAlias The entity type or alias to shutdown. If null main shutdown will be triggered
     *
     * @static
     * @return void
     */
    public static function shutdown($entityTypeOrAlias = null)
    {
        // get own instance
        $self = self::getInstance();
        // check if entityKey is null to do a full shutdown
        if (is_null($entityTypeOrAlias)) {
            // iterate all registered entities execute shutdown
            foreach ($self as $entityKey => $entityInstance) {
                // check if it is a real entity key
                if (strpos($entityKey, self::ENTITY_KEY_PREFIX) !== false) {
                    // set shutdown signal
                    $entityInstance->__execute(self::EX_CMD_SHUTDOWN);
                }
            }
            // wait for all entity instance to shutdown properly
            foreach ($self as $entityKey => $entityInstance) {
                // check if it is a real entity key
                if (strpos($entityKey, self::ENTITY_KEY_PREFIX) !== false) {
                    // wait for proper shutdown
                    $entityInstance->join();
                    // delete ref from internal storage
                    unset($self["{$entityKey}"]);
                }
            }
            // shutdown own instance
            $self->__execute(self::EX_CMD_SHUTDOWN);
            // remove globals ref for itself
            unset($GLOBALS[self::getGlobalVarName()]);
            // wait for itself
            return $self->join();
        }
        // in every other case just shutdown the entity given as entityKey
        if ($entityInstance = self::getEntity($entityTypeOrAlias)) {
            $entityInstance->__execute(self::EX_CMD_SHUTDOWN);
            // wait for instance propert shutdown
            $entityInstance->join();
            // delete ref from internal storage
            $entityKey = self::getEntityKey($entityTypeOrAlias);
            unset($self["{$entityKey}"]);
        }
    }

    /**
     * Return a valid variable name to be set as global variable
     * based on own class name with automatic namespace cutoff
     *
     * @static
     * @return string
     */
    public static function getGlobalVarName()
    {
        return '__' . strtolower(__CLASS__);
    }

    /**
     * Returns an entity from internal storage
     *
     * @param string $entityTypeOrAlias The type or aliase to get
     *
     * @throws \AppserverIo\Concurrency\ExecutorService\Exception
     * @return ExecutorService
     */
    public static function getEntity($entityTypeOrAlias)
    {
        // get own instance
        $self = self::getInstance();
        // init entityKey
        $entityKey = self::getEntityKey($entityTypeOrAlias);
        // check if entity does not exists
        if (!isset($self["{$entityKey}"])) {
            throw new ExecutorService\Exception(sprintf("Entity '%s' does not exist.", $entityTypeOrAlias));
        }
        // return it
        return $self["{$entityKey}"];
    }

    /**
     * Returns the entities key that will be used for storing internal refs of instance
     *
     * @param  string $key The key for entity prefix to append
     * @return string
     */
    public static function getEntityKey($key)
    {
        return self::ENTITY_KEY_PREFIX . $key;
    }

    /**
     * Returns the instance created in global scope
     *
     * @static
     * @return GlobalStorage The global storage instance
     *
     * @throws \AppserverIo\Concurrency\ExecutorService\Exception
     */
    public static function getInstance()
    {
        $globalVarName = self::getGlobalVarName();
        global $$globalVarName;
        if (is_null($$globalVarName)) {
            throw new ExecutorService\Exception(sprintf("Failed to get instance '$%s'. Please call init() in global scope first and check if PTHREADS_ALLOW_GLOBALS flag is set in specific Thread start calls.", $globalVarName));
        }
        return $$globalVarName;
    }

    /**
     * Creates a new instance of execution service with given entity class and mapped alias
     *
     * @param string $entityType The class to make use of execution service as singleton
     * @param string $alias      The alias to use for store that instance in global storage of execution service
     * @param string $autoloader The autoloader to use
     *
     * @return ExecutorService
     * @throws \AppserverIo\Concurrency\ExecutorService\Exception
     */
    public static function newFromEntity($entityType, $alias = null, $autoloader = null)
    {
        // get own instance
        $self = self::getInstance();
        // init entity key
        $entityKey = self::getEntityKey($entityType);
        // init autoloader and set default if nothing was given
        if (is_null($autoloader)) {
            $autoloader = $self->__autoloader;
        }

        // check alias functionality
        if (!is_null($alias)) {
            $entityKey = self::getEntityKey($alias);
            // check if alias was registered already
            if (isset($self["{$entityKey}"])) {
                throw new ExecutorService\Exception(sprintf("Entity '%s' with alias '%s' has already been created.", $entityType, $alias));
            }
        } else {
            // check if entity was registered already
            if (isset($self["{$entityKey}"])) {
                throw new ExecutorService\Exception(sprintf("Entity '%s' has already been created.", $entityType, $alias));
            }
        }

        // create execution service instance with entity.
        $newInstanceFromEntity = new ExecutorService($entityType, $autoloader);

        // set ref to local storage
        $self["{$entityKey}"] = $newInstanceFromEntity;

        // return it
        return $newInstanceFromEntity;
    }

    /**
     * Initializes all entity method annotations
     *
     * @param ExecutorService $executorServiceInstance An executor service instance
     * @param string          $entityClassName         The entity class name to init
     *
     * @static
     * @return array
     */
    public static function initEntityAnnotations($executorServiceInstance, $entityClassName)
    {
        // get reflection of entity class
        $reflector = new \ReflectionClass($entityClassName);
        // get all Methods
        $methods = $reflector->getMethods();
        // init method exec type mapper array
        $methodExecutionTypeMapper = array();
        // iterate all methods
        foreach ($methods as $method) {
            // set default method type
            $methodExecutionTypeMapper["::{$method->getName()}"] =  self::EX_METHOD_DEFAULT;
            // get method annotations
            preg_match_all('#@(.*?)\n#s', $method->getDocComment(), $annotations);
            // check if asynch annotation
            if (in_array('Asynchronous', $annotations[1])) {
                $methodExecutionTypeMapper["::{$method->getName()}"] = self::EX_METHOD_ASYNCHRONOUS;
            }
            // check if synch annotation
            if (in_array('Synchronized', $annotations[1])) {
                $methodExecutionTypeMapper["::{$method->getName()}"] = self::EX_METHOD_SYNCHRONIZED;
            }
            // check if direct annotation
            if (in_array('Direct', $annotations[1])) {
                $methodExecutionTypeMapper["::{$method->getName()}"] = self::EX_METHOD_DIRECT;
            }
        }
        // save mapper array to executor service instance
        $executorServiceInstance->__methodExecutionTypeMapper = $methodExecutionTypeMapper;
    }
}