/***************************************************************** JADE - Java Agent DEvelopment Framework is a framework to develop multi-agent systems in compliance with the FIPA specifications. Copyright (C) 2000 CSELT S.p.A. GNU Lesser General Public License This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, version 2.1 of the License. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *****************************************************************/ package jade.domain; //#MIDP_EXCLUDE_FILE import java.util.Enumeration; import java.util.Vector; import java.util.Date; import jade.util.leap.HashMap; import jade.util.leap.ArrayList; import jade.util.leap.List; import jade.util.leap.Iterator; import jade.util.leap.Properties; import jade.util.Logger; import java.net.InetAddress; import jade.core.AID; import jade.core.behaviours.*; import jade.domain.FIPAAgentManagement.*; import jade.domain.FIPAAgentManagement.InternalError; import jade.domain.JADEAgentManagement.*; import jade.domain.KBManagement.*; import jade.domain.DFGUIManagement.*; import jade.domain.DFGUIManagement.GetDescription; // Explicitly imported to avoid conflict with FIPA management ontology //#PJAVA_EXCLUDE_BEGIN import jade.domain.introspection.AMSSubscriber; import jade.domain.introspection.Event; import jade.domain.introspection.IntrospectionVocabulary; import jade.domain.introspection.DeadAgent; //#PJAVA_EXCLUDE_END import jade.lang.acl.ACLMessage; import jade.lang.acl.MessageTemplate; import jade.lang.acl.ISO8601; import jade.gui.GuiAgent; import jade.gui.GuiEvent; import jade.proto.SubscriptionResponder; import jade.proto.AchieveREInitiator; import jade.content.*; import jade.content.lang.*; import jade.content.lang.sl.*; import jade.content.onto.basic.*; /** <p> Standard <em>Directory Facilitator</em> agent. This class implements <em><b>FIPA</b></em> <em>DF</em> agent. <b>JADE</b> applications typically don't use this class directly, but interact with the DF agent through <em>ACL</em> message passing. The <code>DFService</code> class provides a number of static methods that facilitate this task. More <em>DF</em> agents can be created by application programmers to divide a platform into many <em><b>Agent Domains</b></em>. <p> A DF agent accepts a number of optional configuration parameters that can be set either as command line options or within a properties file (to be passed to the DF as an argument). </p> <table border="1" cellspacing="0"> <tr> <th>Parameter</th> <th>Description</th> </tr> <tr> <td> <code>jade_domain_df_autocleanup</code> </td> <td> If set to <code>true</code>, indicates that the DF will automatically clean up registrations as soon as an agent terminates. The default is <code>false</code> </td> </tr> <tr> <td> <code>jade_domain_df_maxleasetime</code> </td> <td> Indicates the maximum lease time (in millisecond) that the DF will grant for agent description registrations (defaults to infinite). </td> </tr> <tr> <td> <code>jade_domain_df_maxresult</code> </td> <td> Indicates the maximum number of items found in a search operation that the DF will return to the requester (defaults to 100). </td> </tr> <tr> <td> <code>jade_domain_df_disablevalidation</code> </td> <td> Turns off content validation on incoming/outgoing messages (defaults to false) </td> </tr> <tr> <td> <code>jade_domain_df_db-default</code> </td> <td> If set to <code>true</code>, indicates that the DF will store its catalogue into an internal HSQL database, running within the same VM. (The HSQL jar files have to added to the Java CLASSPATH) </td> </tr> <tr> <td> <code>jade_domain_df_db-url</code> </td> <td> Indicates the JDBC URL of the database the DF will store its catalogue into. This parameter is ignored if <code>jade_domain_df_db-default</code> is set. If neither this parameter nor <code>jade_domain_df_db-default</code> is specified the DF will keep its catalogue in memory. </td> </tr> <tr> <td> <code>jade_domain_df_db-driver</code> </td> <td> Indicates the JDBC driver to be used to access the DF database (defaults to the ODBC-JDBC bridge). This parameter is ignored if <code>jade_domain_df_db-url</code> is not set or <code>jade_domain_df_db-default</code> is set. </td> </tr> <tr> <td> <code>jade_domain_df_db-username</code>, <code>jade_domain_df_db-password</code> </td> <td> Indicate the username and password to be used to access the DF database (default to null). These parameters are ignored if <code>jade_domain_df_db-url</code> is not set or <code>jade_domain_df_db-default</code> is set. </td> </tr> <td> <code>jade_domain_df_db-cleantables</code> </td> <td> If set to <code>true</code>, indicates that the DF will clean the content of all pre-existing database tables, used by the DF. This parameter is ignored if the catalogue is not stored in a database. </td> </tr> <tr> <td> <code>jade_domain_df_db-abortonerror</code> </td> <td> If set to <code>true</code>, indicates that the DF will immediately terminate in case it cannot connect to the database. This parameter is ignored if the catalogue is not stored in a database. </td> </tr> <tr> <td> <code>jade_domain_df_kb-factory</code> </td> <td> Indicates the name of the factory class that should be used to create the knowledge base objects for the DF. The class has to be a sub class of jade.domain.DFKBFactory. </td> </tr> <tr> <td> <code>jade_domain_df_poolsize</code> </td> <td> The dimension of the pool of thread dedicated to serving registration, deregistration and search requests. If <code>0</code> (default) is specified then registration, deregistration and search requests are served directly by the df agent Thread. This parameter is ignored when using a volatile (in-memory) knowledge base. </td> </tr> </table> <p> For instance the following command line will launch a JADE main container with a DF that will store its catalogue into a database accessible at URL jdbc:odbc:dfdb and that will keep agent registrations for 1 hour at most. <code> java jade.Boot -gui -jade_domain_df_db-url jdbc:odbc:dfdb -jade_domain_df_maxleasetime 3600000 </code> <p> Each DF has a GUI but, by default, it is not visible. The GUI of the agent platform includes a menu item that allows to show the GUI of the default DF. In order to show the GUI, you should simply send the following message to each DF agent: <code>(request :content (action DFName (SHOWGUI)) :ontology JADE-Agent-Management :protocol fipa-request)</code> @see DFService @author Giovanni Rimassa - Universita' di Parma @author Tiziana Trucco - TILAB S.p.A. @author Elisabetta Cortese - TILAB S.p.A. @author Giovanni Caire - TILAB @author Roland Mungenast - Profactor @version $Date: 2007-03-30 18:30:14 +0200 (ven, 30 mar 2007) $ $Revision: 5952 $ */ public class df extends GuiAgent implements DFGUIAdapter { //#APIDOC_EXCLUDE_BEGIN // FIXME The size of the cache must be read from the Profile private final static int SEARCH_ID_CACHE_SIZE = 16; private jade.util.HashCache searchIdCache = new jade.util.HashCache(SEARCH_ID_CACHE_SIZE); private int searchIdCnt = 0; // The DF federated with this DF private List children = new ArrayList(); // The DF this DF is federated with private List parents = new ArrayList(); // Maps a parent DF to the description used by this DF to federate with that parent private HashMap dscDFParentMap = new HashMap(); // Maps an action that is being serviced by a Behaviour to the // request message that activated the Behaviour and the notification message // to be sent back (as soon as the Behaviour will complete) to the requester private HashMap pendingRequests = new HashMap(); // The GUI of this DF private DFGUIInterface gui; // Current description of this df private DFAgentDescription myDescription = null; private Codec codec = new SLCodec(); //#PJAVA_EXCLUDE_BEGIN // This is used in case a pool-size != 0 is specified to serve FIPA requests private ThreadedBehaviourFactory tbf; private AMSSubscriber amsSubscriber; //#PJAVA_EXCLUDE_END // Configuration parameter keys private static final String AUTOCLEANUP = "jade_domain_df_autocleanup"; private static final String POOLSIZE = "jade_domain_df_poolsize"; private static final String MAX_LEASE_TIME = "jade_domain_df_maxleasetime"; private static final String MAX_RESULTS = "jade_domain_df_maxresult"; private static final String DISABLE_VALIDATION = "jade_domain_df_disablevalidation"; private static final String DB_DRIVER = "jade_domain_df_db-driver"; private static final String DB_URL = "jade_domain_df_db-url"; private static final String DB_USERNAME = "jade_domain_df_db-username"; private static final String DB_PASSWORD = "jade_domain_df_db-password"; private static final String KB_FACTORY = "jade_domain_df_kb-factory"; private static final String DB_DEFAULT = "jade_domain_df_db-default"; private static final String DB_CLEANTABLES = "jade_domain_df_db-cleantables"; private static final String DB_ABORTONERROR = "jade_domain_df_db-abortonerror"; // Limit of searchConstraints.maxresult // FIPA Agent Management Specification doc num: SC00023J (6.1.4 Search Constraints) // a negative value of maxresults indicates that the sender agent is willing to receive // all available results private static final String DEFAULT_MAX_RESULTS = "100"; /* * This is the actual value for the limit on the maximum number of results to be * returned in case of an ulimited search. This value is read from the Profile, * if no value is set in the Profile, then DEFAULT_MAX_RESULTS is used instead. */ private int maxResultLimit = Integer.parseInt(DEFAULT_MAX_RESULTS); private Date maxLeaseTime = null; private KB agentDescriptions = null; private KBSubscriptionManager subManager = null; private SubscriptionResponder dfSubscriptionResponder; private Logger logger; /** Default constructor. This constructor does nothing; however, applications can create their own DF agents using regular platform management commands. Moreover, customized versions of a Directory Facilitator agent can be built subclassing this class and exploiting its protected interface. */ public df() { } /** This method starts all behaviours needed by <em>DF</em> agent to perform its role within <em><b>JADE</b></em> agent platform. */ protected void setup() { logger = Logger.getMyLogger(getLocalName()); String sDisableValidation = getProperty(DISABLE_VALIDATION, "false"); //#PJAVA_EXCLUDE_BEGIN // ---------- Read configuration ---------- // If an argument is specified, it indicates the name of a properties // file where to read DF configuration from. Otherwise configuration // properties are read from the Profile. // Values in a property file override those in the profile if // both are specified. String sAutocleanup = getProperty(AUTOCLEANUP, null); String sPoolsize = getProperty(POOLSIZE, null); String sMaxLeaseTime = getProperty(MAX_LEASE_TIME, null); String sMaxResults = getProperty(MAX_RESULTS, DEFAULT_MAX_RESULTS); String dbUrl = getProperty(DB_URL, null); String dbDriver = getProperty(DB_DRIVER, null); String dbUsername = getProperty(DB_USERNAME, null); String dbPassword = getProperty(DB_PASSWORD, null); String kbFactClass = getProperty(KB_FACTORY, null); String sDBDefault = getProperty(DB_DEFAULT, null); String sCleanTables = getProperty(DB_CLEANTABLES, null); String sDBAbortOnError = getProperty(DB_ABORTONERROR, null); Object[] args = this.getArguments(); if(args != null && args.length > 0) { Properties p = new Properties(); try { p.load((String) args[0]); sAutocleanup = p.getProperty(AUTOCLEANUP, sAutocleanup); sPoolsize = p.getProperty(POOLSIZE, sPoolsize); sMaxLeaseTime = p.getProperty(MAX_LEASE_TIME, sMaxLeaseTime); sMaxResults = p.getProperty(MAX_RESULTS, sMaxResults); sDisableValidation = p.getProperty(DISABLE_VALIDATION, sDisableValidation); dbUrl = p.getProperty(DB_URL, dbUrl); dbDriver = p.getProperty(DB_DRIVER, dbDriver); dbUsername = p.getProperty(DB_USERNAME, dbUsername); dbPassword = p.getProperty(DB_PASSWORD, dbPassword); kbFactClass = p.getProperty(KB_FACTORY, kbFactClass); sDBDefault = p.getProperty(DB_DEFAULT, sDBDefault); sCleanTables = p.getProperty(DB_CLEANTABLES, sCleanTables); sDBAbortOnError = p.getProperty(DB_ABORTONERROR, sDBAbortOnError); } catch (Exception e) { logger.log(Logger.SEVERE,"Error loading configuration from file "+args[0]+" ["+e+"]."); } } // ---------- Max lease time ---------- try { maxLeaseTime = new Date(Long.parseLong(sMaxLeaseTime)); } catch (Exception e) { // Keep default } logger.log(Logger.CONFIG, "DF Max lease time = "+(maxLeaseTime != null ? ISO8601.toRelativeTimeString(maxLeaseTime.getTime()) : "infinite")); // ---------- Max search results ---------- try { maxResultLimit = Integer.parseInt(sMaxResults); if(maxResultLimit < 0){ maxResultLimit = Integer.parseInt(DEFAULT_MAX_RESULTS); logger.log(Logger.WARNING,"The maxResult parameter of the DF Search Constraints can't be a negative value. It has been set to the default value: " + DEFAULT_MAX_RESULTS); }else if(maxResultLimit > Integer.parseInt(DEFAULT_MAX_RESULTS)){ logger.log(Logger.WARNING,"Setting the maxResult of the DF Search Constraint to large values can cause low performance or system crash !!"); } } catch (Exception e) { // Keep default } logger.log(Logger.CONFIG, "DF Max search result = "+maxResultLimit); // ---------- Knowledge Base ---------- StringBuffer sb = new StringBuffer("DF KB configuration:\n"); // ---------- KB Factory ---------- DFKBFactory kbFactory = new DFKBFactory(); // Default factory if (kbFactClass != null) { try { Object o = Class.forName(kbFactClass).newInstance(); if (o instanceof DFKBFactory) { kbFactory = (DFKBFactory)o; sb.append("- Factory class = " + kbFactClass); sb.append('\n'); } else { logger.log(Logger.SEVERE,"The class " + kbFactClass + " is not a valid KB factory for the DF."); } } catch (Exception e) { logger.log(Logger.SEVERE, "Error loading class " + kbFactClass + ". "+e); } } // ---------- Persistent KB ---------- boolean cleanTables = getBooleanProperty(sCleanTables, DB_CLEANTABLES); if (isRestarting()) { // Avoid cleaing tables if we are restarting cleanTables = false; } boolean dbDefault = getBooleanProperty(sDBDefault, DB_DEFAULT); boolean dbAbortOnError = getBooleanProperty(sDBAbortOnError, DB_ABORTONERROR); if (dbDefault) { sb.append("- Type = persistent using internal HSQL database\n"); try { agentDescriptions = kbFactory.getDFDBKB(maxResultLimit, null, null, null, null, cleanTables); } catch (Exception e) { logger.log(Logger.SEVERE,"Error creating persistent KB based on internal HSQLDB database", e); if (dbAbortOnError) { doDelete(); return; } else { logger.log(Logger.WARNING, "DF using volatile (in-memory) KB"); sb = new StringBuffer("DF KB configuration:\n"); } } } if (agentDescriptions == null && dbUrl != null) { if(logger.isLoggable(Logger.CONFIG)){ sb.append("- Type = persistent\n"); sb.append("- DB url = "+dbUrl); sb.append('\n'); sb.append("- DB driver = "+dbDriver); sb.append('\n'); sb.append("- DB username = "+dbUsername); sb.append('\n'); sb.append("- DB password = "+dbPassword); sb.append('\n'); } try { agentDescriptions = kbFactory.getDFDBKB(maxResultLimit, dbDriver, dbUrl, dbUsername, dbPassword, cleanTables); } catch (Exception e) { logger.log(Logger.SEVERE,"Error creating persistent KB (+url = "+getValue(dbUrl)+", driver = "+getValue(dbDriver)+", username = "+getValue(dbUsername)+", password = "+getValue(dbPassword)+")", e); if (dbAbortOnError) { doDelete(); return; } else { logger.log(Logger.WARNING, "DF using volatile (in-memory) KB"); sb = new StringBuffer("DF KB configuration:\n"); } } } // ---------- Volatile (in-memory) KB ---------- if (agentDescriptions == null){ sb.append("- Type = volatile\n"); agentDescriptions = kbFactory.getDFMemKB(maxResultLimit); if (sPoolsize != null) { logger.log(Logger.WARNING, "Ignoring pool-size indication ("+sPoolsize+"). Parameter not supported when using volatile KB"); sPoolsize = null; } } logger.log(Logger.CONFIG, sb.toString()); //#PJAVA_EXCLUDE_END /*#PJAVA_INCLUDE_BEGIN agentDescriptions = new DFMemKB(Integer.parseInt(getProperty(MAX_RESULTS, DEFAULT_MAX_RESULTS))); #PJAVA_INCLUDE_END*/ // Initiate the SubscriptionManager used by the DF subManager = new KBSubscriptionManager(agentDescriptions); subManager.setContentManager(getContentManager()); // Register languages and ontologies getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL0); getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL1); getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL2); getContentManager().registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL); getContentManager().registerOntology(FIPAManagementOntology.getInstance()); getContentManager().registerOntology(JADEManagementOntology.getInstance()); getContentManager().registerOntology(DFAppletOntology.getInstance()); boolean disableValidation = getBooleanProperty(sDisableValidation, DISABLE_VALIDATION); if (disableValidation) { getContentManager().setValidationMode(false); } // Create and add behaviours MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.REQUEST); MessageTemplate mt1 = null; // Behaviour dealing with FIPA management actions mt1 = MessageTemplate.and(mt, MessageTemplate.MatchOntology(FIPAManagementOntology.getInstance().getName())); mt1 = MessageTemplate.and(mt1, MessageTemplate.not(MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.ITERATED_FIPA_REQUEST))); //#PJAVA_EXCLUDE_BEGIN int poolSize = getIntegerProperty(sPoolsize, POOLSIZE); if (poolSize == 0) { DFFipaAgentManagementBehaviour fipaRequestResponder = new DFFipaAgentManagementBehaviour(this, mt1); addBehaviour(fipaRequestResponder); } else { logger.log(Logger.INFO, "DF FIPA request pool-size = "+poolSize); tbf = new ThreadedBehaviourFactory(); for (int i = 0; i < poolSize; ++i) { DFFipaAgentManagementBehaviour fipaRequestResponder = new DFFipaAgentManagementBehaviour(this, mt1); fipaRequestResponder.setBehaviourName(getLocalName()+"#FIPAManagementResponder-"+i); addBehaviour(tbf.wrap(fipaRequestResponder)); } } //#PJAVA_EXCLUDE_END /*#PJAVA_INCLUDE_BEGIN DFFipaAgentManagementBehaviour fipaRequestResponder = new DFFipaAgentManagementBehaviour(this, mt1); addBehaviour(fipaRequestResponder); #PJAVA_INCLUDE_END*/ // Behaviour dealing with iterated searches according to the iterated-fipa-request protocol mt1 = MessageTemplate.and(mt, MessageTemplate.MatchOntology(FIPAManagementOntology.getInstance().getName())); mt1 = MessageTemplate.and(mt1, MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.ITERATED_FIPA_REQUEST)); DFIteratedSearchManagementBehaviour iteratedSearchResponder = new DFIteratedSearchManagementBehaviour(this, mt1); addBehaviour(iteratedSearchResponder); // Behaviour dealing with JADE management actions mt1 = MessageTemplate.and(mt, MessageTemplate.MatchOntology(JADEManagementOntology.getInstance().getName())); DFJadeAgentManagementBehaviour jadeRequestResponder = new DFJadeAgentManagementBehaviour(this, mt1); addBehaviour(jadeRequestResponder); // Behaviour dealing with DFApplet management actions mt1 = MessageTemplate.and(mt, MessageTemplate.MatchOntology(DFAppletOntology.getInstance().getName())); DFAppletManagementBehaviour appletRequestResponder = new DFAppletManagementBehaviour(this, mt1); addBehaviour(appletRequestResponder); // Behaviour dealing with subscriptions mt1 = MessageTemplate.and( MessageTemplate.MatchOntology(FIPAManagementOntology.getInstance().getName()), MessageTemplate.or(MessageTemplate.MatchPerformative(ACLMessage.SUBSCRIBE), MessageTemplate.MatchPerformative(ACLMessage.CANCEL))); dfSubscriptionResponder = new SubscriptionResponder(this, mt1, subManager) { // If the CANCEL message has a meaningful content, use it. // Otherwise deregister the Subscription with the same convID (default) protected ACLMessage handleCancel(ACLMessage cancel) throws FailureException { try { Action act = (Action) myAgent.getContentManager().extractContent(cancel); ACLMessage subsMsg = (ACLMessage)act.getAction(); Subscription s = getSubscription(subsMsg); if (s != null) { mySubscriptionManager.deregister(s); s.close(); } } catch(Exception e) { if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Unknown CANCEL content. Use default handler"); super.handleCancel(cancel); } return null; } }; addBehaviour(dfSubscriptionResponder); // Set the DFDescription of thie DF setDescriptionOfThisDF(getDefaultDescription()); // Set lease policy and subscription responder to the knowledge base agentDescriptions.setSubscriptionResponder(dfSubscriptionResponder); agentDescriptions.setLeaseManager(new LeaseManager() { public Date getLeaseTime(Object item){ return ((DFAgentDescription) item).getLeaseTime(); } public void setLeaseTime(Object item, Date lease){ ((DFAgentDescription) item).setLeaseTime(lease); } /** * Grant a lease to this request according to the * policy of the DF: if the requested lease is * greater than the max lease policy of this DF, then * the granted lease is set to the max of the DF. **/ public Object grantLeaseTime(Object item){ if (maxLeaseTime != null) { Date lease = getLeaseTime(item); long current = System.currentTimeMillis(); if ( (lease != null && lease.getTime() > (current+maxLeaseTime.getTime())) || ( (lease == null) && (maxLeaseTime != null))) { // the first condition of this if considers the case when the agent requestes a leasetime greater than the policy of this DF. The second condition, instead, considers the case when the agent requests an infinite leasetime and the policy of this DF does not allow infinite leases. setLeaseTime(item, new Date(current+maxLeaseTime.getTime())); } } return item; } public boolean isExpired(Date lease){ return (lease != null && (lease.getTime() <= System.currentTimeMillis())); } } ); // Prepare the default description of this DF (used for federations) myDescription = getDefaultDescription(); //#PJAVA_EXCLUDE_BEGIN boolean autocleanup = getBooleanProperty(sAutocleanup, AUTOCLEANUP); if (autocleanup) { logger.log(Logger.CONFIG,"Autocleanup activated"); // Finally add the behaviour that listens for AMS notifications // about dead agents. amsSubscriber = new AMSSubscriber() { protected void installHandlers(java.util.Map handlersTable) { handlersTable.put(IntrospectionVocabulary.DEADAGENT, new EventHandler() { public void handle(Event ev) { DeadAgent da = (DeadAgent)ev; AID id = da.getAgent(); // Deregister the dead agent in case it was registered try { DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(id); DFDeregister(dfd); } catch (Exception e) { // Just do nothing } // Unsubscribe the dead agent in case it was subscribed unsubscribeDeadAgent(id); } }); } }; addBehaviour(amsSubscriber); } //#PJAVA_EXCLUDE_END // In case some subscription is already present in the KB (e.g. because the DF is using a DB-based KB and // it has just restarted) reinitialize the SubscriptionResponder protocol. // NOTE that this must be done after all components taking part in the subscription mechanism have been properly initialized Enumeration ss = agentDescriptions.getSubscriptions(); while (ss.hasMoreElements()) { SubscriptionResponder.Subscription s = (SubscriptionResponder.Subscription) ss.nextElement(); dfSubscriptionResponder.createSubscription(s.getMessage()); } } // End of method setup() private void unsubscribeDeadAgent(AID id) { Vector ss = dfSubscriptionResponder.getSubscriptions(id); for (int i = 0; i < ss.size(); i++) { try { subManager.deregister((SubscriptionResponder.Subscription) ss.elementAt(i)); } catch (Exception e) { logger.log(Logger.WARNING, "Error deregistering subscription of dead agent "+id.getName(), e); } } } private boolean getBooleanProperty(String sValue, String name) { boolean b = false; if (sValue != null) { try { b = Boolean.valueOf(sValue).booleanValue(); } catch (Exception e) { logger.log(Logger.WARNING, "\""+sValue+"\" is not a valid value for boolean parameter" + name, e); } } return b; } private int getIntegerProperty(String sValue, String name) { int n = 0; if (sValue != null) { try { n = Integer.parseInt(sValue); } catch (Exception e) { logger.log(Logger.WARNING, "\""+sValue+"\" is not a valid value for integer parameter" + name, e); } } return n; } private String getValue(String s) { return (s != null ? s : "null"); } /** Cleanup <em>DF</em> on exit. This method performs all necessary cleanup operations during agent shutdown. */ protected void takeDown() { //#PJAVA_EXCLUDE_BEGIN if (amsSubscriber != null) { // Unsubscribe from the AMS send(amsSubscriber.getCancel()); } if (tbf != null) { tbf.interrupt(); } //#PJAVA_EXCLUDE_END if(gui != null) { gui.disposeAsync(); } DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(getAID()); Iterator it = parents.iterator(); while(it.hasNext()) { AID parentName = (AID)it.next(); try { DFService.deregister(this, parentName, dfd); } catch(FIPAException fe) { fe.printStackTrace(); } } } //////////////////////////////////// // Recursive search //////////////////////////////////// /** Add the behaviour handling a recursive search. If constraints contains a null search_id, then a new one is generated and the new search_id is stored into searchIdCache for later check (i.e. to avoid search loops). */ private void performRecursiveSearch(List localResults, DFAgentDescription dfd, SearchConstraints constraints, Search action){ int maxRes = getActualMaxResults(constraints); int maxDep = constraints.getMaxDepth().intValue(); // Create the new SearchConstraints. SearchConstraints newConstr = new SearchConstraints(); // Max-depth decreased by 1 newConstr.setMaxDepth(new Long ((long) (maxDep - 1))); // Max-results decreased by the number of items found locally newConstr.setMaxResults(new Long((long) (maxRes - localResults.size()))); // New globally unique search-id unless already present String searchId = constraints.getSearchId(); if (searchId == null) { searchId = getName() + String.valueOf(searchIdCnt++) + System.currentTimeMillis(); if (searchIdCnt >= SEARCH_ID_CACHE_SIZE) { searchIdCnt = 0; } searchIdCache.add(searchId); } newConstr.setSearchId(searchId); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Activating recursive search: "+localResults.size()+" item(s) found locally. "+maxRes+" expected. Search depth is "+maxDep+". Search ID is "+searchId+". Propagating search to "+children.size()+" federated DF(s)"); // Add the behaviour handling the search on federated DFs addBehaviour(new RecursiveSearchHandler(localResults, dfd, newConstr, action)); } /** Inner class RecursiveSearchHandler. This is a behaviour handling recursive searches i.e. searches that must be propagated to children (federated) DFs. */ private class RecursiveSearchHandler extends AchieveREInitiator { private static final long DEFAULTTIMEOUT = 300000; // 5 minutes private List results; private DFAgentDescription template; private SearchConstraints constraints; private Search action; private int maxExpectedResults; private int receivedResults; /** Construct a new RecursiveSearchHandler. @param results The search results. Initially this includes the items found locally. @param template The DFAgentDescription used as tamplate for the search. @param constraints The constraints for the search to be propagated. @param action The original Search action. This is used as a key to retrieve the incoming REQUEST message. */ private RecursiveSearchHandler(List results, DFAgentDescription template, SearchConstraints constraints, Search action) { super(df.this, null); this.results = results; this.template = template; this.constraints = constraints; this.action = action; maxExpectedResults = constraints.getMaxResults().intValue(); receivedResults = 0; } /** We broadcast the search REQUEST to all children (federated) DFs in parallel. */ protected Vector prepareRequests(ACLMessage request) { Vector requests = null; ACLMessage incomingRequest = (ACLMessage) pendingRequests.get(action); if (incomingRequest != null) { Date deadline = incomingRequest.getReplyByDate(); if (deadline == null) { deadline = new Date(System.currentTimeMillis() + DEFAULTTIMEOUT); } requests = new Vector(children.size()); Iterator it = children.iterator(); while (it.hasNext()) { AID childDF = (AID) it.next(); ACLMessage msg = DFService.createRequestMessage(myAgent, childDF, FIPAManagementVocabulary.SEARCH, template, constraints); msg.setReplyByDate(deadline); requests.addElement(msg); } } return requests; } /** As long as we receive the replies we update the results. If we reach the max-results we send back the notification to the requester and discard successive replies. */ protected void handleInform(ACLMessage inform) { if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Recursive search result received from "+inform.getSender().getName()+"."); int cnt = 0; if (receivedResults < maxExpectedResults) { try { DFAgentDescription[] dfds = DFService.decodeResult(inform.getContent()); for (int i = 0; i < dfds.length; ++i) { // We add the item only if not already present if (addResult(dfds[i])) { receivedResults++; cnt++; if (receivedResults >= maxExpectedResults) { sendPendingNotification(action, results); } } } } catch (Exception e) { if(logger.isLoggable(Logger.SEVERE)) logger.log(Logger.SEVERE,"WARNING: Error decoding reply from federated DF "+inform.getSender().getName()+" during recursive search ["+e.toString()+"]."); } } if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,cnt+" new items found in recursive search."); } protected void handleRefuse(ACLMessage refuse) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"REFUSE received from federated DF "+refuse.getSender().getName()+" during recursive search."); } protected void handleFailure(ACLMessage failure) { // FIXME: In general this is due to a federation loop (search-id already used) // In this case no warning must be printed --> We should use FINE log level in that case if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"FAILURE received from federated DF "+failure.getSender().getName()+" during recursive search."); } protected void handleNotUnderstood(ACLMessage notUnderstood) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"NOT_UNDERSTOOD received from federated DF "+notUnderstood.getSender().getName()+" during recursive search."); } protected void handleOutOfSequence(ACLMessage msg) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"Out of sequence message "+ACLMessage.getPerformative(msg.getPerformative())+" received from "+msg.getSender().getName()+" during recursive search."); } public int onEnd() { // Send back the notification to the originator of the // search (unless already sent) if (receivedResults < maxExpectedResults) { sendPendingNotification(action, results); } return super.onEnd(); } private boolean addResult(DFAgentDescription newDfd) { Iterator it = results.iterator(); while (it.hasNext()) { DFAgentDescription dfd = (DFAgentDescription) it.next(); if (dfd.getName().equals(newDfd.getName())) { return false; } } results.add(newDfd); return true; } } // END of inner class RecursiveSearchHandler ////////////////////////////////////////////////////// // Methods actually accessing the DF Knowledge base ////////////////////////////////////////////////////// void DFRegister(DFAgentDescription dfd) throws AlreadyRegistered { //checkMandatorySlots(FIPAAgentManagementOntology.REGISTER, dfd); Object old = agentDescriptions.register(dfd.getName(), dfd); if(old != null) throw new AlreadyRegistered(); if(isADF(dfd)) { if(logger.isLoggable(Logger.INFO)) logger.log(Logger.INFO,"Added federation "+dfd.getName().getName()+" --> "+getName()); children.add(dfd.getName()); try { gui.addChildren(dfd.getName()); } catch (Exception ex) {} } // for subscriptions subManager.handleChange(dfd, null); try{ //refresh the GUI if shown, exception thrown if the GUI was not shown gui.addAgentDesc(dfd.getName()); gui.showStatusMsg("Registration of agent: " + dfd.getName().getName() + " done."); }catch(Exception ex){} } //this method is called into the prepareResponse of the DFFipaAgentManagementBehaviour to perform a Deregister action void DFDeregister(DFAgentDescription dfd) throws NotRegistered { //checkMandatorySlots(FIPAAgentManagementOntology.DEREGISTER, dfd); Object old = agentDescriptions.deregister(dfd.getName()); if(old == null) throw new NotRegistered(); if (children.remove(dfd.getName())) try { gui.removeChildren(dfd.getName()); } catch (Exception e) {} // for subscriptions dfd.clearAllServices(); //clear all services since we are deregistering subManager.handleChange(dfd, (DFAgentDescription)old); try{ // refresh the GUI if shown, exception thrown if the GUI was not shown // this refresh must be here, otherwise the GUI is not synchronized with // registration/deregistration made without using the GUI gui.removeAgentDesc(dfd.getName(),df.this.getAID()); gui.showStatusMsg("Deregistration of agent: " + dfd.getName().getName() +" done."); }catch(Exception e1){} } void DFModify(DFAgentDescription dfd) throws NotRegistered { //checkMandatorySlots(FIPAAgentManagementOntology.MODIFY, dfd); Object old = agentDescriptions.register(dfd.getName(), dfd); if(old == null) { // Rollback agentDescriptions.deregister(dfd.getName()); throw new NotRegistered(); } // for subscription subManager.handleChange(dfd, (DFAgentDescription)old); try{ gui.removeAgentDesc(dfd.getName(), df.this.getAID()); gui.addAgentDesc(dfd.getName()); gui.showStatusMsg("Modify of agent: "+dfd.getName().getName() + " done."); }catch(Exception e){} } List DFSearch(DFAgentDescription dfd, int maxResults) { return agentDescriptions.search(dfd, maxResults); } KBIterator DFIteratedSearch(DFAgentDescription dfd) { return agentDescriptions.iterator(dfd); } //////////////////////////////////////////////////////////////// // Methods serving the actions of the FIPA Management ontology //////////////////////////////////////////////////////////////// /** Serve the Register action of the FIPA management ontology. Package scoped since it is called by DFFipaAgentManagementBehaviour. */ void registerAction(Register r, AID requester) throws FIPAException { DFAgentDescription dfd = (DFAgentDescription) r.getDescription(); // Check mandatory slots DFService.checkIsValid(dfd, true); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action Register for "+dfd.getName()); // Avoid autoregistration if (dfd.getName().equals(getAID())) { throw new Unauthorised(); } // Do it DFRegister(dfd); } /** Serve the Deregister action of the FIPA management ontology. Package scoped since it is called by DFFipaAgentManagementBehaviour. */ void deregisterAction(Deregister d, AID requester) throws FIPAException { DFAgentDescription dfd = (DFAgentDescription) d.getDescription(); // Check mandatory slots DFService.checkIsValid(dfd, false); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action Deregister for "+dfd.getName()); // Do it DFDeregister(dfd); } /** Serve the Modify action of the FIPA management ontology. Package scoped since it is called by DFFipaAgentManagementBehaviour. */ void modifyAction(Modify m, AID requester) throws FIPAException { DFAgentDescription dfd = (DFAgentDescription) m.getDescription(); // Check mandatory slots DFService.checkIsValid(dfd, true); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action Modify for "+dfd.getName()); // Do it DFModify(dfd); } /** Serve the Search action of the FIPA management ontology. Package scoped since it is called by DFFipaAgentManagementBehaviour. @return the List of descriptions matching the template specified in the Search action. If no description is found an empty List is returned. In case a recursive search is required it returns null to indicate that the result is not yet available. */ List searchAction(Search s, AID requester) throws FIPAException { DFAgentDescription dfd = (DFAgentDescription) s.getDescription(); SearchConstraints constraints = s.getConstraints(); List result = null; if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action Search"); // Avoid loops in searching on federated DFs checkSearchId(constraints.getSearchId()); int maxResult = getActualMaxResults(constraints); // Search locally result = DFSearch(dfd, maxResult); // Note that if the local search produced more results than // required, we don't even consider the recursive search // regardless of the maxDepth parameter. if(result.size() < maxResult) { // Check if the search has to be propagated Long maxDepth = constraints.getMaxDepth(); if ( (children.size() > 0) && (maxDepth != null) && (maxDepth.intValue() > 0) ) { // Start a recursive search. performRecursiveSearch(result, dfd, constraints, s); // The final result will be available at a later time. return null; } } return result; } /** Serve a Search action of the FIPA management ontology requested using an iterated-fipa-request protocol. Package scoped since it is called by DFIteratedSearchManagementBehaviour. @return an iterator over the DFAgentDescription matching the specified search template. */ KBIterator iteratedSearchAction(Search s, AID requester) throws FIPAException { DFAgentDescription dfd = (DFAgentDescription) s.getDescription(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action Iterated-Search"); return DFIteratedSearch(dfd); } //////////////////////////////////////////////////////////////// // Methods serving the actions of the JADE Management ontology //////////////////////////////////////////////////////////////// /** Serve the ShowGui action of the JADE management ontology. Package scoped since it is called by DFJadeAgentManagementBehaviour. @exception FailureException If the GUI is already visible or some error occurs creating the GUI. */ void showGuiAction(ShowGui sg, AID requester) throws FailureException { if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester.getName()+" requesting action ShowGui"); if (!showGui()){ throw new FailureException("Gui_is_being_shown_already"); } } ////////////////////////////////////////////////////////// // Methods serving the actions of the DF-Applet ontology ////////////////////////////////////////////////////////// /** Serve the GetParents action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ List getParentsAction(GetParents action, AID requester) { if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action GetParents."); return parents; } /** Serve the GetDescription action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ List getDescriptionAction(GetDescription action, AID requester) { if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action GetDescription."); // FIXME: This embeds the description into a list since the Applet still expects a List List tmp = new ArrayList(); tmp.add(getDescriptionOfThisDF()); return tmp; } /** Serve the GetDescriptionUsed action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ List getDescriptionUsedAction(GetDescriptionUsed action, AID requester) { AID parent = action.getParentDF(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action GetDescriptionUsed to federate with "+parent.getName()); // FIXME: This embeds the description into a list since the Applet still expects a List List tmp = new ArrayList(); tmp.add(getDescriptionOfThisDF(parent)); return tmp; } /** Serve the Federate action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ void federateAction(final Federate action, AID requester) { AID remoteDF = action.getDf(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action Federate with DF "+remoteDF.getName()); Register r = new Register(); DFAgentDescription tmp = action.getDescription(); final DFAgentDescription dfd = (tmp != null ? tmp : getDescriptionOfThisDF()); r.setDescription(dfd); Behaviour b = new RemoteDFRequester(remoteDF, r) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { addParent(getRemoteDF(), dfd); } sendPendingNotification(action, result); return 0; } }; addBehaviour(b); } /** Serve the RegisterWith action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ void registerWithAction(final RegisterWith action, AID requester){ AID remoteDF = action.getDf(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action RegisterWith on DF "+remoteDF.getName()); Register r = new Register(); final DFAgentDescription dfd = action.getDescription(); r.setDescription(dfd); Behaviour b = new RemoteDFRequester(remoteDF, r) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { if(dfd.getName().equals(myAgent.getAID())) { // The registered agent is the DF itself --> This is a federation addParent(getRemoteDF(), dfd); } } sendPendingNotification(action, result); return 0; } }; addBehaviour(b); } /** Serve the DeregisterFrom action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ void deregisterFromAction(final DeregisterFrom action, AID requester){ AID remoteDF = action.getDf(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action DeregisterFrom on DF "+remoteDF.getName()); Deregister d = new Deregister(); final DFAgentDescription dfd = action.getDescription(); d.setDescription(dfd); Behaviour b = new RemoteDFRequester(remoteDF, d) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { if(dfd.getName().equals(myAgent.getAID())) { // The deregistered agent is the DF itself --> Remove a federation removeParent(getRemoteDF()); } } sendPendingNotification(action, result); return 0; } }; addBehaviour(b); } /** Serve the ModifyOn action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ void modifyOnAction(final ModifyOn action, AID requester){ AID remoteDF = action.getDf(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action ModifyOn on DF "+remoteDF.getName()); Modify m = new Modify(); m.setDescription(action.getDescription()); Behaviour b = new RemoteDFRequester(remoteDF, m) { public int onEnd() { sendPendingNotification(action, getResult()); return 0; } }; addBehaviour(b); } /** Serve the SearchOn action of the DF-Applet ontology Package scoped since it is called by DFAppletManagementBehaviour. */ void searchOnAction(final SearchOn action, AID requester){ AID remoteDF = action.getDf(); if(logger.isLoggable(Logger.CONFIG)) logger.log(Logger.CONFIG,"Agent "+requester+" requesting action SearchOn on DF "+remoteDF.getName()); Search s = new Search(); s.setDescription(action.getDescription()); s.setConstraints(action.getConstraints()); Behaviour b = new RemoteDFRequester(remoteDF, s) { public int onEnd() { sendPendingNotification(action, getResult()); return 0; } }; addBehaviour(b); } //#APIDOC_EXCLUDE_BEGIN /////////////////////////////////////////////////////////// // GUI Management: DFGUIAdapter interface implementation /////////////////////////////////////////////////////////// protected void onGuiEvent(GuiEvent ev) { try { switch(ev.getType()) { case DFGUIAdapter.EXIT: { gui.disposeAsync(); gui = null; doDelete(); break; } case DFGUIAdapter.CLOSEGUI: { gui.disposeAsync(); gui = null; break; } case DFGUIAdapter.REGISTER: { AID df = (AID) ev.getParameter(0); final DFAgentDescription dfd = (DFAgentDescription) ev.getParameter(1); DFService.checkIsValid(dfd, true); if (getAID().equals(df)) { // Register an agent with this DF DFRegister(dfd); } else { // Register an agent with another DF. gui.showStatusMsg("Processing your request & waiting for result..."); Register r = new Register(); r.setDescription(dfd); Behaviour b = new RemoteDFRequester(df, r) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { gui.showStatusMsg("Registration request processed. Ready for new request"); if(dfd.getName().equals(myAgent.getAID())) { // The registered agent is the DF itself --> This is a federation addParent(getRemoteDF(), dfd); } } else { gui.showStatusMsg("Error processing request. "+((InternalError) result).getMessage()); } return 0; } }; addBehaviour(b); } break; } case DFGUIAdapter.DEREGISTER: { AID df = (AID) ev.getParameter(0); final DFAgentDescription dfd = (DFAgentDescription) ev.getParameter(1); DFService.checkIsValid(dfd, false); if (getAID().equals(df)) { // Deregister an agent with this DF DFDeregister(dfd); } else { // Deregister an agent with another DF. gui.showStatusMsg("Processing your request & waiting for result..."); Deregister d = new Deregister(); d.setDescription(dfd); Behaviour b = new RemoteDFRequester(df, d) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { gui.showStatusMsg("Deregistration request processed. Ready for new request"); if(dfd.getName().equals(myAgent.getAID())) { // The deregistered agent is the DF itself --> Remove a federation removeParent(getRemoteDF()); } else { gui.removeSearchResult(dfd.getName()); } } else { gui.showStatusMsg("Error processing request. "+((InternalError) result).getMessage()); } return 0; } }; addBehaviour(b); } break; } case DFGUIAdapter.MODIFY: { AID df = (AID) ev.getParameter(0); DFAgentDescription dfd = (DFAgentDescription) ev.getParameter(1); DFService.checkIsValid(dfd, true); if (getAID().equals(df)) { // Modify the description of an agent with this DF DFModify(dfd); } else { // Modify the description of an agent with another DF gui.showStatusMsg("Processing your request & waiting for result..."); Modify m = new Modify(); m.setDescription(dfd); Behaviour b = new RemoteDFRequester(df, m) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { gui.showStatusMsg("Modification request processed. Ready for new request"); } else { gui.showStatusMsg("Error processing request. "+((InternalError) result).getMessage()); } return 0; } }; addBehaviour(b); } break; } case DFGUIAdapter.SEARCH: { AID df = (AID) ev.getParameter(0); DFAgentDescription dfd = (DFAgentDescription) ev.getParameter(1); SearchConstraints sc = (SearchConstraints)ev.getParameter(2); // Note that we activate a RemoteDFBehaviour even if the DF to perform // the search on is the local DF. This allows handling recursive // search (if needed) correctly gui.showStatusMsg("Processing your request & waiting for result..."); Search s = new Search(); s.setDescription(dfd); s.setConstraints(sc); Behaviour b = new RemoteDFRequester(df, s) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { gui.showStatusMsg("Search request processed. Ready for new request"); gui.refreshLastSearchResults((List) result, getRemoteDF()); } else { gui.showStatusMsg("Error processing request. "+((InternalError) result).getMessage()); } return 0; } }; addBehaviour(b); break; } case DFGUIAdapter.FEDERATE: { AID df = (AID) ev.getParameter(0); final DFAgentDescription dfd = (DFAgentDescription) ev.getParameter(1); gui.showStatusMsg("Processing your request & waiting for result..."); Register r = new Register(); r.setDescription(dfd); Behaviour b = new RemoteDFRequester(df, r) { public int onEnd() { Object result = getResult(); if (!(result instanceof InternalError)) { gui.showStatusMsg("Federation request processed. Ready for new request"); addParent(getRemoteDF(), dfd); } else { gui.showStatusMsg("Error processing request. "+((InternalError) result).getMessage()); } return 0; } }; addBehaviour(b); break; } } // END of switch } // END of try catch(FIPAException fe) { gui.showStatusMsg("Error processing request. "+fe.getMessage()); fe.printStackTrace(); } } //#APIDOC_EXCLUDE_END /** This method returns the description of an agent registered with the DF. */ public DFAgentDescription getDFAgentDsc(AID name) throws FIPAException { DFAgentDescription template = new DFAgentDescription(); template.setName(name); List l = agentDescriptions.search(template, 1); if(l.isEmpty()) return null; else return (DFAgentDescription)l.get(0); } /** * This method returns the current description of this DF */ public DFAgentDescription getDescriptionOfThisDF() { return myDescription; } /** * This method returns the description of this df used to federate with the given parent */ public DFAgentDescription getDescriptionOfThisDF(AID parent) { return (DFAgentDescription)dscDFParentMap.get(parent); } ///////////////////////////////////// // Utility methods ///////////////////////////////////// /** This method make visible the GUI of the DF. @return true if the GUI was not visible already, false otherwise. */ protected boolean showGui() { if (gui == null) { try{ Class c = Class.forName("jade.tools.dfgui.DFGUI"); gui = (DFGUIInterface)c.newInstance(); gui.setAdapter(df.this); //this method must be called to avoid reflection (the constructor of the df gui has no parameters). DFAgentDescription matchEverything = new DFAgentDescription(); // FIXME: Getting a list with all DFAgentDescriptions may cause out of memory // The DFGui should probably be paged and we should use an iterated search. List agents = agentDescriptions.search(matchEverything, -1); List AIDList = new ArrayList(); Iterator it = agents.iterator(); while(it.hasNext()) AIDList.add(((DFAgentDescription)it.next()).getName()); gui.refresh(AIDList.iterator(), parents.iterator(), children.iterator()); gui.setVisible(true); return true; }catch(Exception e){e.printStackTrace();} } return false; } /** * This method creates the DFAgent descriptor for this df used to federate with other df. */ private DFAgentDescription getDefaultDescription() { DFAgentDescription out = new DFAgentDescription(); out.setName(getAID()); out.addOntologies(FIPAManagementOntology.getInstance().getName()); out.addLanguages(FIPANames.ContentLanguage.FIPA_SL0); out.addProtocols(FIPANames.InteractionProtocol.FIPA_REQUEST); ServiceDescription sd = new ServiceDescription(); sd.setName("df-service"); sd.setType("fipa-df"); sd.addOntologies(FIPAManagementOntology.getInstance().getName()); sd.addLanguages(FIPANames.ContentLanguage.FIPA_SL0); sd.addProtocols(FIPANames.InteractionProtocol.FIPA_REQUEST); try{ sd.setOwnership(InetAddress.getLocalHost().getHostName()); }catch (java.net.UnknownHostException uhe){ sd.setOwnership("unknown"); } out.addServices(sd); return out; } /** * This method set the description of the df according to the DFAgentDescription passed. * The programmers can call this method to provide a different initialization of the description of the df they are implementing. * The method is called inside the setup of the agent and set the df description using a default description. */ protected void setDescriptionOfThisDF(DFAgentDescription dfd) { myDescription = dfd; myDescription.setName(getAID()); if (!isADF(myDescription)) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"The description set for this DF does not include a \"fipa-df\" service."); } } /** * This method can be used to add a parent (a DF this DF is federated with). * @param dfName the parent df (the df with which this df has been registered) * @param dfd the description used by this df to register with the parent. */ protected void addParent(AID dfName, DFAgentDescription dfd) { parents.add(dfName); if(gui != null) // the gui can be null if this method is called in order to manage a request made by the df-applet. gui.addParent(dfName); dscDFParentMap.put(dfName,dfd); //update the table of corrispondence between parents and description of this df used to federate. } /** this method can be used to remove a parent (a DF with which this DF is federated). */ protected void removeParent(AID dfName) { parents.remove(dfName); if(gui != null) //the gui can be null is this method is called in order to manage a request from the df applet gui.removeParent(dfName); dscDFParentMap.remove(dfName); } /** Store the request message related to an action that is being processed by a Behaviour. This information will be used by the Behaviour to send back the notification to the requester. */ void storePendingRequest(Object key, ACLMessage request) { pendingRequests.put(key, request); } void removePendingRequest(Object key) { pendingRequests.remove(key); } /** Send the notification related to an action that has been processed by a Behaviour. */ private void sendPendingNotification(Concept action, Object result) { ACLMessage request = (ACLMessage) pendingRequests.remove(action); if (request != null) { ACLMessage notification = request.createReply(); ContentElement ce = null; Action act = new Action(getAID(), action); if (result instanceof InternalError) { // Some error occurred during action processing notification.setPerformative(ACLMessage.FAILURE); ContentElementList cel = new ContentElementList(); cel.add(act); cel.add((Predicate) result); ce = cel; } else { // Action processing was OK notification.setPerformative(ACLMessage.INFORM); if (result != null) { ce = new Result(act, result); } else { ce = new Done(act); } } try { getContentManager().fillContent(notification, ce); send(notification); AID receiver = (AID) notification.getAllReceiver().next(); if(logger.isLoggable(Logger.FINE)) logger.log(Logger.FINE,"Notification sent back to "+receiver.getName()); } catch (Exception e) { // Should never happen if(logger.isLoggable(Logger.SEVERE)) logger.log(Logger.SEVERE,"Error encoding pending notification content."); e.printStackTrace(); } } else { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"Processed action request not found."); } } /** * @return * <ul> * <li> 1 if constraints.maxResults == null (according to FIPA specs) * <li> maxResultLimit if constraints.maxResults is infinite (i.e. < 0) or * greater than maxResultLimit * <li> constraints.maxResults otherwise * </ul> * This is package-scoped since it is also used by the DFIteratedSearchManagementBehaviour **/ int getActualMaxResults(SearchConstraints constraints) { int maxResult = (constraints.getMaxResults() == null ? 1 : constraints.getMaxResults().intValue()); maxResult = ((maxResult < 0 || maxResult > maxResultLimit) ? maxResultLimit : maxResult); // limit the max num of results return maxResult; } /** Check if this search must be served, i.e. if it has not yet been received. In particular the value of search_id must be different from any prior value that was received. If search_id is not null and it has not yet been received, search_id is added into the cache. @exception FIPAException if the search id is already in the cache. */ private void checkSearchId(String searchId) throws FIPAException { if (searchId != null) { if (searchIdCache.contains(searchId)) { throw new InternalError("search-id already served"); } else { searchIdCache.add(searchId); } } } private boolean isADF(DFAgentDescription dfd) { try { return ((ServiceDescription)dfd.getAllServices().next()).getType().equalsIgnoreCase("fipa-df"); } catch (Exception e) { return false; } } //#APIDOC_EXCLUDE_END }