package org.drools.grid.services; import java.rmi.RemoteException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; import org.drools.grid.ConnectorException; import org.drools.grid.DirectoryNodeService; import org.drools.grid.GenericConnection; import org.drools.grid.GenericNodeConnector; import org.drools.grid.services.configuration.GenericProvider; import org.drools.grid.services.factory.DirectoryInstanceFactory; import org.drools.grid.services.factory.ExecutionEnvironmentFactory; import org.drools.grid.services.factory.TaskServerInstanceFactory; import org.drools.grid.services.strategies.DirectoryInstanceByPrioritySelectionStrategy; import org.drools.grid.services.strategies.DirectoryInstanceSelectionStrategy; import org.drools.grid.services.strategies.ExecutionEnvByPrioritySelectionStrategy; import org.drools.grid.services.strategies.ExecutionEnvironmentSelectionStrategy; import org.drools.grid.services.strategies.TaskServerInstanceByPrioritySelectionStrategy; import org.drools.grid.services.strategies.TaskServerInstanceSelectionStrategy; /** * @author salaboy * * This class will represent a Grid Topology where we can execute and manager our * knowledge sessions. * A Grid Topology can be conformed using different types of nodes/resources depending on * your business requirements and architecture. * The grid topology can contains a set of the following type of nodes: * - Execution Environments: we will use them to host knowledge sessions (runtime) * - Directory Instances: we will use them to keep track of the current nodes in our topology * - Task Servers Instance: we will use them to execute and manage Human Tasks for business processes * * From the user perspective the GridTopology will be normally constructed with a HelperClass called: GridTopologyFactory * This helper class will be in charge of reading the GridTopology configuration and based on that * create the GridTopology object with all the nodes registered. * * It's important to understand that the GridTopologyConfiguration will represent a static description/configuration of a * runtime environment. Based on this static description/configuration the GridTopology object will represent a * live status of this runtime environment. * * * */ public class GridTopology { private String topologyName; private Map<String, ExecutionEnvironment> executionEnvironments = new ConcurrentHashMap<String, ExecutionEnvironment>(); private Map<String, String> executionEnvironmentsByConnectorId = new ConcurrentHashMap<String, String>(); private Map<String, DirectoryInstance> directoryInstances = new ConcurrentHashMap<String, DirectoryInstance>(); private Map<String, TaskServerInstance> taskServerInstances = new ConcurrentHashMap<String, TaskServerInstance>(); private final ExecutionEnvironmentSelectionStrategy DEFAULT_EXECTUTION_STRATEGY = new ExecutionEnvByPrioritySelectionStrategy(); private final DirectoryInstanceSelectionStrategy DEFAULT_DIRECTORY_STRATEGY = new DirectoryInstanceByPrioritySelectionStrategy(); private final TaskServerInstanceSelectionStrategy DEFAULT_TASK_STRATEGY = new TaskServerInstanceByPrioritySelectionStrategy(); /* * Create a new Grid Topology * @param topologyName */ public GridTopology(String topologyName) { this.topologyName = topologyName; } /* * Get the GridTopology name */ public String getTopologyName() { return this.topologyName; } /* * @param name: name associated to this ExecutionEnvironment * @param provider: used to create a new ExecutionEnvironment instance * This method will register a new Execution Environment based on the configured Provider. * The provider will contain all the information to be able to establish a connection with it. * The following steps are executed inside this method: * 1) Create the new ExecutionEnvironment object that will represent a remote host for our knowledge sessions. * 2) Each ExecutionEnvironment will have an underlaying connection to support remote/distribtued interactions * 3) for each Execution Environment registered in this topology * 3.1) We need to inject the reference from the newly created Execution Enviroment to the existing ones * 3.2) We need to inject the reference from all the existing Execution Environments to the newly created * 4) for each Directory Instance registered in this topology * 4.1) We need to inject the reference from the newly created Execution Environment in each exisiting Directory * 4.2) We need to inject a reference from each existing directory to the newly created Execution Environment * 5) Add the newly created Execution Environment to the topology maps. We keep to maps for Execution Environments * to be able to look based on the underlaying connector and based on the defined Execution Environment name. * 6) Register the Execution Environment inside all the currently available Directory Instances */ public void registerExecutionEnvironment(String name, GenericProvider provider) { ExecutionEnvironment environment = ExecutionEnvironmentFactory.newExecutionEnvironment( name, provider ); GenericNodeConnector connector = environment.getConnector(); GenericConnection connection = connector.getConnection(); connection.addExecutionNode( connector ); for ( ExecutionEnvironment e : this.executionEnvironments.values() ) { connection.addExecutionNode( e.getConnector() ); e.getConnector().getConnection().addExecutionNode( connector ); } for ( DirectoryInstance d : this.directoryInstances.values() ) { connection.addDirectoryNode( d.getConnector() ); d.getConnector().getConnection().addExecutionNode( connector ); } this.executionEnvironments.put( name, environment ); try { connector.connect(); this.executionEnvironmentsByConnectorId.put( connector.getId(), environment.getName() ); connector.disconnect(); } catch ( ConnectorException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } registerResourceInDirectories( name, provider.getId() ); } /* * @param name: of the ExecutionEnvironment to unregister * This method unregister the Execution Environment from this running instance of the grid topology * based on the name. The following steps are executed in order to unregister * an ExecutionEnvironment from the GridTopology: * 1) Get the ExecutionEnvironment from the executionEnvironmentMap * 2) Get the ExecutionEnvironment connector * 2.1) Remove the ExecutionEnvironment from the executionEnvironmentByConnectorId map * 2.2) Dispose the connection that the connector contains * 2.3) Disconnect the connector * 3) Remove the executionEnvironment from the executionEnvironmentMap * 4) Unregister the ExecutionEnvironment from the running Directory Instances * */ public void unregisterExecutionEnvironment(String name) { ExecutionEnvironment ee = this.executionEnvironments.get( name ); try { GenericNodeConnector connector = ee.getConnector(); this.executionEnvironmentsByConnectorId.remove( connector.getId() ); connector.getConnection().dispose(); connector.disconnect(); } catch ( ConnectorException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } this.executionEnvironments.remove( name ); unregisterResourceFromDirectories( name ); } /* * Get Execution Environment by Name * @param name: String Execution Environment Name */ public ExecutionEnvironment getExecutionEnvironment(String name) { return this.executionEnvironments.get( name ); } /* * Get ExecutionEnvironment by connector * @param connector: using this connector id, this method will return an ExecutionEnvironment */ public ExecutionEnvironment getExecutionEnvironment(GenericNodeConnector connector) { ExecutionEnvironment ee = null; try { String eeName = this.executionEnvironmentsByConnectorId.get( connector.getId() ); ee = this.executionEnvironments.get( eeName ); } catch ( ConnectorException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } return ee; } /* * Get the Best ExecutionEnvironment available based on a ExecutionEnvironmentSelectionStrategy * @param strategy: it's an implementation of the ExecutionEnvironmentSelectionStrategy interface */ public ExecutionEnvironment getBestExecutionEnvironment(ExecutionEnvironmentSelectionStrategy strategy) { return strategy.getBestExecutionEnvironment( this.executionEnvironments ); } /* * Get the Best ExecutionEnvironment available based on the default ExecutionEnvironmentSelectionStrategy */ public ExecutionEnvironment getExecutionEnvironment() { return this.DEFAULT_EXECTUTION_STRATEGY.getBestExecutionEnvironment( this.executionEnvironments ); } /* * This method register a new Directory instance based on the information provided by the GenericProvider * The provider will contain all the information to be able to establish a connection with it. * The following steps are executed inside this method: * 1) Create the new DirectoryInstance object that will represent map that will store information about * the resources that are currently running in our topology. * 2) Each DirectoryInstance will have an underlaying connection to support remote/distribtued interactions * 3) for each Execution Environment registered in this topology * 3.1) We need to inject the reference from the newly created Directory to the existing ExecutionEnvironments * 3.2) We need to inject the reference from all the existing Execution Environments to the newly created Directory Instance * 4) for each Directory Instance registered in this topology * 4.1) We need to inject the reference from the newly created Directory Instance in each exisiting Directory * 4.2) We need to inject a reference from each existing directory to the newly created DirectoryInstance * 5) Add the newly created Directory Instance to the topology map. We keep a map for Directory Instances * to be able to lookup based on the Directory Instance name. * 6) Register the Directory Instance inside all the currently available Directory Instances */ public void registerDirectoryInstance(String name, GenericProvider provider) { DirectoryInstance directory = DirectoryInstanceFactory.newDirectoryInstance( name, provider ); GenericNodeConnector connector = directory.getConnector(); GenericConnection connection = connector.getConnection(); connection.addDirectoryNode( connector ); for ( ExecutionEnvironment e : this.executionEnvironments.values() ) { e.getConnector().getConnection().addDirectoryNode( connector ); connection.addExecutionNode( e.getConnector() ); } for ( DirectoryInstance d : this.directoryInstances.values() ) { d.getConnector().getConnection().addDirectoryNode( connector ); connection.addDirectoryNode( d.getConnector() ); } this.directoryInstances.put( name, directory ); registerResourceInDirectories( name, provider.getId() ); } /* * Get the Best DirectoryInstance based on a DirectoryInstanceSelectionStrategy * @param strategy it's the strategy used to choose the best DirectoryInstance available */ public DirectoryInstance getBestDirectoryInstance(DirectoryInstanceSelectionStrategy strategy) { return strategy.getBestDirectoryInstance( this.directoryInstances ); } /* * Get the Directory Instance based on a default strategy */ public DirectoryInstance getDirectoryInstance() { return this.DEFAULT_DIRECTORY_STRATEGY.getBestDirectoryInstance( this.directoryInstances ); } /* * Get a Directory Instance by Name */ public DirectoryInstance getDirectoryInstance(String name) { return this.directoryInstances.get( name ); } /* * Unregister a Directroy Instance from this running GridTopology * This method unregister the Directory Instance from this running instance of the grid topology * based on the name. The following steps are executed in order to unregister * a DirectoryInstance from the GridTopology: * 1) Get the Directory Instance from the directoryInstances Map * 2) Get the DirectoryInstance connector * 2.1) Dispose the internal connection from the connector * 2.2) Disconnect the connector * 3) Unregister the DirectoryInstance from the running Directory Instances * 4) Remove the DirectoryInstance from the directoryInstances Map */ public void unregisterDirectoryInstance(String name) { DirectoryInstance dir = this.directoryInstances.get( name ); GenericNodeConnector connector = dir.getConnector(); try { connector.getConnection().dispose(); connector.disconnect(); } catch ( ConnectorException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } unregisterResourceFromDirectories( name ); this.directoryInstances.remove( name ); } /* * This method will register a new Task Server based on the configured Provider. * The provider will contain all the information to be able to establish a connection with the Task Server. * The following steps are executed inside this method: * 1) Create the new TaskServerInstance object that will represent a remote service to execute human tasks for * business processes. * 2) Each TaskServiceInstance will have an underlaying connection to support remote/distribtued interactions * 3) for each Execution Environment registered in this topology * 3.1) We need to inject the reference from the newly created Task Server Instance to the existing ExecutionEnvironments * 3.2) We need to inject the reference from all the existing Execution Environments to the newly created TaskServer Instance * 4) for each Directory Instance registered in this topology * 4.1) We need to inject the reference from the newly created TaskServiceInstance in each exisiting Directory * 4.2) We need to inject a reference from each existing directory to the newly created TaskServerInstance * 5) Add the newly created TaskServerInstance to the topology maps. * 6) Register the TaskServer Instance inside all the currently available Directory Instances */ public void registerTaskServerInstance(String name, GenericProvider provider) { TaskServerInstance taskServer = TaskServerInstanceFactory.newTaskServerInstance( name, provider ); GenericNodeConnector connector = taskServer.getConnector(); GenericConnection connection = connector.getConnection(); connection.addHumanTaskNode( connector ); for ( ExecutionEnvironment e : this.executionEnvironments.values() ) { e.getConnector().getConnection().addHumanTaskNode( connector ); connection.addExecutionNode( e.getConnector() ); } for ( DirectoryInstance d : this.directoryInstances.values() ) { d.getConnector().getConnection().addHumanTaskNode( connector ); connection.addDirectoryNode( d.getConnector() ); } this.taskServerInstances.put( name, taskServer ); registerResourceInDirectories( name, provider.getId() ); } /* * Get TaskServer Instance by name * @param name: it's the name used to register a specific task server instance */ public TaskServerInstance getTaskServerInstance(String name) { return this.taskServerInstances.get( name ); } /* * Get the best task server instance available based on a strategy * @param strategy: a TaskServerInstanceSelectionStrategy it's used to retrieve the best instance * currently available */ public TaskServerInstance getBestTaskServerInstance(TaskServerInstanceSelectionStrategy strategy) { return strategy.getBestTaskServerInstance( this.taskServerInstances ); } /* * Get the best task server instance based on the default selection strategy */ public TaskServerInstance getBestTaskServerInstance() { return this.DEFAULT_TASK_STRATEGY.getBestTaskServerInstance( this.taskServerInstances ); } /* * @param name: of the TaskServiceInstance that we want to unregister * Unregister a TaskServer Instance from this running GridTopology * This method unregister the TaskServer Instance from this running instance of the grid topology * based on the name. The following steps are executed in order to unregister * a TaskServerInstance from the GridTopology: * 1) Get the TaskServer Instance from the taskServerInstances Map * 2) Get the TaskServer Instance connector * 2.1) Dispose the internal connection from the connector * 2.2) Disconnect the connector * 3) Remove the TaskServerInstance from the directoryInstances Map * 4) Unregister the TaskServer Instance from the running DirectoryInstances */ public void unregisterTaskServerInstance(String name) { TaskServerInstance taskServer = this.taskServerInstances.get( name ); GenericNodeConnector connector = taskServer.getConnector(); try { connector.getConnection().dispose(); connector.disconnect(); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } catch ( ConnectorException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } this.taskServerInstances.remove( name ); unregisterResourceFromDirectories( name ); } /* * Close and Dispose all connections and connectors to remote services * without removing/unregister the references from those services */ public void disconnect() throws ConnectorException, RemoteException { disconnectExecutionEnvironments(); disconnectDirectoryInstances(); disconnectTaskServerInstances(); } /* * Disconnect all the taskServerInstances ongoing connections */ private void disconnectTaskServerInstances() throws ConnectorException, RemoteException { for ( String key : this.taskServerInstances.keySet() ) { TaskServerInstance taskServer = this.taskServerInstances.get( key ); GenericNodeConnector connector = taskServer.getConnector(); connector.getConnection().dispose(); connector.disconnect(); } } /* * Disconnect all the directoryInstances ongoing connections */ private void disconnectDirectoryInstances() throws RemoteException, ConnectorException { for ( String key : this.directoryInstances.keySet() ) { DirectoryInstance dir = this.directoryInstances.get( key ); GenericNodeConnector connector = dir.getConnector(); connector.getConnection().dispose(); connector.disconnect(); } } /* * Disconnect all the executionEnvironments ongoing connections */ private void disconnectExecutionEnvironments() throws ConnectorException, RemoteException { for ( String key : this.executionEnvironments.keySet() ) { ExecutionEnvironment ee = this.executionEnvironments.get( key ); GenericNodeConnector connector = ee.getConnector(); connector.getConnection().dispose(); connector.disconnect(); } } /* * Clean the GridTopology object to be disposed */ public void dispose() throws ConnectorException, RemoteException { System.out.println("I'm Disposing the topology!!!"); for ( String key : this.executionEnvironments.keySet() ) { // unregisterExecutionEnvironment( key ); } for ( String key : this.directoryInstances.keySet() ) { // unregisterDirectoryInstance( key ); } for ( String key : this.taskServerInstances.keySet() ) { // unregisterTaskServerInstance( key ); } } /* * @param name: that will be associated with the resource id * @param resourceId: Id of the resource that we want to associate with the name * Register the resource id (ExecutionEnvironment, DirectoryInstance, TaskServerInstance) * inside all the current available directory instances. */ private void registerResourceInDirectories(String name, String resourceId) { for ( DirectoryInstance directory : this.directoryInstances.values() ) { try { DirectoryNodeService directoryNode = directory.getDirectoryNode().get( DirectoryNodeService.class ); if ( directoryNode != null ) { try { directoryNode.register( name, resourceId ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } } try { directory.getConnector().disconnect(); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } } catch ( ConnectorException e ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, e ); } } } /* * @param name: of the resource that we want to unregister from current Directories * Unregister a resource (ExecutionEnvironment, DirectoryInstance, TaskServerInstance) * from all the current available directory instances. */ private void unregisterResourceFromDirectories(String name) { for ( DirectoryInstance directory : this.directoryInstances.values() ) { try { DirectoryNodeService directoryNode = directory.getDirectoryNode().get( DirectoryNodeService.class ); if ( directoryNode != null ) { try { directoryNode.unregister( name ); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } } try { directory.getConnector().disconnect(); } catch ( RemoteException ex ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, ex ); } } catch ( ConnectorException e ) { Logger.getLogger( GridTopology.class.getName() ).log( Level.SEVERE, null, e ); } } } }