package eis.rmi;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Vector;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import eis.AgentListener;
import eis.EnvironmentListener;
import eis.exceptions.ActException;
import eis.exceptions.AgentException;
import eis.exceptions.EntityException;
import eis.exceptions.ManagementException;
import eis.exceptions.NoEnvironmentException;
import eis.exceptions.PerceiveException;
import eis.exceptions.RelationException;
import eis.iilang.Action;
import eis.iilang.EnvironmentCommand;
import eis.iilang.Parameter;
import eis.iilang.Percept;
// TODO remove redundancy between this class and EIDefaultImpl
public abstract class EIServerDefaultImpl implements EIServerRemote {
/**
* This is a list of registered agents.
* <p/>
* Only registered agents can act and be associated with entities.
*/
private LinkedList<String> registeredAgents = null;
/**
* This is a list of entities.
*/
private LinkedList<String> entities = null;
/**
* This is a list of entities, that are not associated with any agent.
*/
private LinkedList<String> freeEntities = null;
/**
* This map stores the agents-entities-relation.
*/
private ConcurrentHashMap<String,HashSet<String>> agentsToEntities = null;
/**
* Stores for each entity its respective type.
*/
private HashMap<String,String> entitiesToTypes = null;
public static boolean debug = true;
private Vector<EIClientRemote> remoteClientListeners = new Vector<EIClientRemote>();
public EIServerDefaultImpl() {
super();
registeredAgents = new LinkedList<String>();
entities = new LinkedList<String>();
freeEntities = new LinkedList<String>();
agentsToEntities = new ConcurrentHashMap<String,HashSet<String>>();
entitiesToTypes = new HashMap<String,String>();
// creating registry
try {
debugPrintln("Creating registry...");
LocateRegistry.createRegistry( Registry.REGISTRY_PORT );
debugPrintln("Exporting...");
EIServerDefaultImpl server = this;
EIServerRemote stub = (EIServerRemote) UnicastRemoteObject.exportObject( server, 0 );
//RemoteServer.setLog( System.out );
debugPrintln("Rebinding registry...");
Registry registry = LocateRegistry.getRegistry();
registry.rebind( "EIServer", stub );
debugPrintln("Server established!");
}
catch ( RemoteException e ) {
debugPrintln("Caught an exception");
debugPrintln(e);
}
}
public void debugPrintln(Object obj) {
if( debug )
System.out.println("[SERVER]: " + obj);
}
public void attachClientListener(EIClientRemote client) throws RemoteException {
debugPrintln("Registered client: " + client);
remoteClientListeners.add(client);
}
public void detachClientListener(EIClientRemote client) throws RemoteException {
remoteClientListeners.remove(client);
}
protected void notifyDeletedEntity(String entity) {
for(EIClientRemote client : remoteClientListeners ) {
try {
client.notifyDeletedEntity(entity);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected void notifyFreeEntity(String entity) {
for(EIClientRemote client : remoteClientListeners ) {
try {
client.notifyFreeEntity(entity);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
protected void notifyNewEntity(String entity) {
for(EIClientRemote client : remoteClientListeners ) {
try {
client.notifyNewEntity(entity);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#associateEntity(java.lang.String, java.lang.String)
*/
public void associateEntity(String agent, String entity) throws RelationException {
// check if exists
if( !entities.contains(entity) )
throw new RelationException("Entity \"" + entity + "\" does not exist!");
if( !registeredAgents.contains(agent) )
throw new RelationException("Agent \"" + entity + "\" has not been registered!");
// check if associated
if( !freeEntities.contains(entity) )
throw new RelationException("Entity \"" + entity + "\" has already been associated!");
// remove
freeEntities.remove(entity);
// associate
HashSet<String> ens = agentsToEntities.get(agent);
if( ens == null ) {
ens = new HashSet<String>();
}
ens.add(entity);
agentsToEntities.put(agent, ens);
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#freeEntity(java.lang.String)
*/
public void freeEntity(String entity) throws RelationException {
// check if exists
if( !entities.contains(entity) )
throw new RelationException("Entity \"" + entity + "\" does not exist!");
// find the association and remove
boolean associated = false;
for( Entry<String,HashSet<String>> entry : agentsToEntities.entrySet()) {
String agent = entry.getKey();
HashSet<String> ens = entry.getValue();
if( ens.contains(entity) ) {
ens.remove(entity);
agentsToEntities.put(agent, ens);
associated = true;
break;
}
}
// fail if entity has not been associated
if( associated == false)
throw new RelationException("Entity \"" + entity + "\" has not been associated!");
// add to free entites
freeEntities.add(entity);
// notify
notifyFreeEntity(entity);
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#freeAgent(java.lang.String)
*/
public void freeAgent(String agent) throws RelationException {
// check if exists
if( !registeredAgents.contains(agent) )
throw new RelationException("Agent \"" + agent + "\" does not exist!");
HashSet<String> ens = agentsToEntities.get(agent);
this.freeEntities.addAll(ens);
// notify
for( String en : ens )
notifyFreeEntity(en);
agentsToEntities.remove(agent);
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#freePair(java.lang.String, java.lang.String)
*/
public void freePair(String agent, String entity) throws RelationException {
// check if exists
if( !registeredAgents.contains(agent) )
throw new RelationException("Agent \"" + agent + "\" does not exist!");
// check if exists
if( !entities.contains(entity) )
throw new RelationException("Entity \"" + entity + "\" does not exist!");
HashSet<String> ens = agentsToEntities.get(agent);
if ( ens == null || ens.contains(entity) == false)
throw new RelationException("Agent \"" + agent + " is not associated with entity \"" + entity + "\"!");
// update mapping
ens.remove(entity);
agentsToEntities.put(agent,ens);
// store as free entity
this.freeEntities.add(entity);
// notify
for( String en : ens )
notifyFreeEntity(en);
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getAssociatedEntities(java.lang.String)
*/
public HashSet<String> getAssociatedEntities(String agent) throws AgentException {
if( registeredAgents.contains(agent) == false )
throw new AgentException("Agent \"" + agent + "\" has not been registered.");
HashSet<String> ret = this.agentsToEntities.get(agent);
if( ret == null)
ret = new HashSet<String>();
return ret;
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getAssociatedAgents(java.lang.String)
*/
public HashSet<String> getAssociatedAgents(String entity) throws EntityException {
if( entities.contains(entity) == false )
throw new EntityException("Entity \"" + entity + "\" has not been registered.");
HashSet<String> ret = new HashSet<String>();
for( Entry<String, HashSet<String>> entry : agentsToEntities.entrySet() ) {
if( entry.getValue().contains(entity) )
ret.add(entry.getKey());
}
return ret;
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getFreeEntities()
*/
@SuppressWarnings("unchecked")
public LinkedList<String> getFreeEntities() {
return (LinkedList<String>)freeEntities.clone();
}
/*
* Acting/perceiving functionality.
*/
// TODO use freeAgent here
// TODO maybe use isConnencted here
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#performAction(java.lang.String, eis.iilang.Action, java.lang.String[])
*/
public LinkedList<Percept> performAction(String agent, Action action, String...entities)
throws ActException, NoEnvironmentException {
// unregistered agents cannot act
if( registeredAgents.contains(agent) == false )
throw new ActException("Agent \"" + agent + "\" is not registered." );
// get the associated entities
HashSet<String> associatedEntities = agentsToEntities.get(agent);
// no associated entity/ies -> trivial reject
if( associatedEntities == null || associatedEntities.size() == 0 )
throw new ActException("Agent \"" + agent + "\" has no associated entities." );
// entities that should perform the action
HashSet<String> targetEntities = null;
if( entities.length == 0 ) {
targetEntities = associatedEntities;
}
else {
targetEntities = new HashSet<String>();
for( String entity : entities ) {
if( associatedEntities.contains(entity) == false)
throw new ActException("Entity \"" + entity + "\" is not associated to agent \"" + agent + "\"." );
targetEntities.add(entity);
}
}
// get the parameters
LinkedList<Parameter> params = action.getParameters();
// targetEntities contains all entities that should perform the action
// params contains all parameters
// determine class parameters for finding the method
// and store the parameters as objects
Class<?>[] classParams = new Class[params.size()+1];
classParams[0] = String.class; // entity name
for( int a = 0 ; a < params.size() ; a++ )
classParams[a+1] = params.get(a).getClass();
// return value
LinkedList<Percept> rets = new LinkedList<Percept>();
try {
// lookup the method
Method m = this.getClass().getMethod("action" + action.getName(),classParams);
if( Class.forName("eis.iilang.Percept").isAssignableFrom(m.getReturnType()) == false)
throw new ActException("Wrong return-type");
// invoke
for( String entity : targetEntities ) {
Object[] objParams = new Object[params.size()+1];
objParams[0] = entity; // entity name
for( int a = 0 ; a < params.size() ; a++ )
objParams[a+1] = params.get(a);
Percept ret = (Percept) m.invoke(this, objParams );
rets.add( ret );
}
} catch (ClassNotFoundException e) {
throw new ActException("Class not found", e);
} catch (SecurityException e) {
throw new ActException("Security exception", e);
} catch (NoSuchMethodException e) {
throw new ActException("No such method", e);
} catch (IllegalArgumentException e) {
throw new ActException("Illegal argument", e);
} catch (IllegalAccessException e) {
throw new ActException("Illegal access", e);
} catch (InvocationTargetException e) {
// action has failed -> let fail
if(e.getCause() instanceof ActException )
throw (ActException) e.getCause(); // rethrow
else if(e.getCause() instanceof NoEnvironmentException)
throw (NoEnvironmentException) e.getCause(); // rethrow
throw new ActException("Invocation target exception", e);
}
return rets;
}
// TODO maybe use isConnencted here
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getAllPercepts(java.lang.String, java.lang.String[])
*/
public LinkedList<Percept> getAllPercepts(String agent, String...entities)
throws PerceiveException, NoEnvironmentException {
// fail if ther agent is not registered
if( registeredAgents.contains(agent) == false)
throw new PerceiveException("Agent \"" + agent + "\" is not registered.");
// get the associated entities
HashSet<String> associatedEntities = agentsToEntities.get(agent);
// fail if there are no associated entities
if( associatedEntities == null || associatedEntities.size() == 0 )
throw new PerceiveException("Agent \"" + agent + "\" has no associated entities.");
// return value
LinkedList<Percept> ret = new LinkedList<Percept>();
// gather all percepts
if( entities.length == 0 ) {
for( String entity : associatedEntities )
ret.addAll( getAllPerceptsFromEntity(entity) );
}
// only from specified entities
else {
for( String entity : entities) {
if( associatedEntities.contains(entity) == false)
throw new PerceiveException("Entity \"" + entity + "\" has not been associated with the agent \"" + agent + "\".");
ret.addAll( getAllPerceptsFromEntity(entity) );
}
}
return ret;
}
/**
* Gets all percepts of an entity.
* <p/>
* This method must be overridden.
*
* @param entity is the entity whose percepts should be retrieved.
* @return a list of percepts.
*/
protected abstract LinkedList<Percept> getAllPerceptsFromEntity(String entity) throws PerceiveException, NoEnvironmentException;
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getEntities()
*/
@SuppressWarnings("unchecked")
public LinkedList<String> getEntities() {
return (LinkedList<String>)entities.clone();
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getType(java.lang.String)
*/
public String getType(String entity) throws EntityException {
if( !this.entities.contains(entity) )
throw new EntityException("Entity \"" + entity + "\" does not exist!");
String type = entitiesToTypes.get(entity);
if( type == null )
type = "unknown";
return type;
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#registerAgent(java.lang.String)
*/
public void registerAgent(String agent) throws AgentException {
if (registeredAgents.contains(agent))
throw new AgentException("Agent " + agent
+ " has already registered to the environment.");
registeredAgents.add(agent);
}
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#unregisterAgent(java.lang.String)
*/
public void unregisterAgent(String agent) throws AgentException {
// fail if agents is not registered
if (!registeredAgents.contains(agent))
throw new AgentException("Agent " + agent
+ " has not registered to the environment.");
// remove from mapping, might be null
agentsToEntities.remove(agent);
// finally remove from registered list
registeredAgents.remove(agent);
}
/*
* Entity functionality. Adding and removing entities.
*/
/* (non-Javadoc)
* @see eis.EnvironmentInterfaceStandard#getAgents()
*/
@SuppressWarnings("unchecked")
public LinkedList<String> getAgents() {
return (LinkedList<String>)registeredAgents.clone();
}
/**
* Adds an entity to the environment.
*
* @param entity is the identifier of the entity that is to be added.
* @throws PlatformException is thrown if the entity already exists.
*/
protected void addEntity(String entity) throws EntityException {
// fail if entity does exist
if( entities.contains(entity) )
throw new EntityException("Entity \"" + entity + "\" does already exist");
// add
entities.add(entity);
freeEntities.add(entity);
// notify
notifyNewEntity(entity);
}
/**
* Deletes an entity, by removing its id from the internal list, and disassociating
* it from the respective agent.
*
* @param entity the id of the entity that is to be removed.
* @throws PlatformException if the agent does not exist.
*/
// TODO use freeEntity here
protected void deleteEntity(String entity) throws EntityException {
// fail if entity does not exist
if( !entities.contains(entity) )
throw new EntityException("Entity \"" + entity + "\" does not exist");
// find the association and remove
for( Entry<String,HashSet<String>> entry : agentsToEntities.entrySet()) {
String agent = entry.getKey();
HashSet<String> ens = entry.getValue();
if( ens.contains(entity) ) {
ens.remove(entity);
agentsToEntities.put(agent, ens);
break;
}
}
// finally delete
entities.remove(entity);
freeEntities.remove(entity);
// notify
notifyDeletedEntity(entity);
}
}