/** * 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; //#J2ME_EXCLUDE_FILE //#APIDOC_EXCLUDE_FILE import jade.core.AID; import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.domain.FIPAAgentManagement.NotUnderstoodException; import jade.domain.FIPAAgentManagement.Property; import jade.domain.FIPAAgentManagement.ServiceDescription; import jade.domain.KBManagement.*; import jade.lang.acl.ACLMessage; import jade.lang.acl.ACLCodec; import jade.lang.acl.StringACLCodec; import jade.proto.SubscriptionResponder; import jade.util.leap.Collection; import jade.util.leap.Iterator; import jade.util.leap.ArrayList; import jade.util.leap.List; import jade.util.leap.Properties; import jade.util.Logger; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.rmi.server.UID; import java.security.MessageDigest; import java.sql.*; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Date; import java.util.Vector; import java.util.NoSuchElementException; import org.apache.commons.codec.binary.Base64; /** * This class implements a knowledge base used by the DF which stores its content * in an external database. * * @author Elisabetta Cortese - TILab * @author Roland Mungenast - Profactor */ public class DFDBKB extends DBKB { private static final int MAX_PRELOAD_CNT = 1000; private static final int MAX_REGISTER_WITHOUT_CLEAN = 100; private static final int MAX_PROP_LENGTH = 255; // Table names private static final String SUBSCRIPTION = "subscription"; private static final String SERVICEPROTOCOL = "serviceprotocol"; private static final String SERVICEONTOLOGY = "serviceontology"; private static final String SERVICELANGUAGE = "servicelanguage"; private static final String SERVICEPROPERTY = "serviceproperty"; private static final String SERVICE = "service"; private static final String LANGUAGE = "language"; private static final String ONTOLOGY = "ontology"; private static final String PROTOCOL = "protocol"; private static final String AGENTUSERDEFSLOT = "agentuserdefslot"; private static final String AGENTRESOLVER = "agentresolver"; private static final String AGENTADDRESS = "agentaddress"; private static final String DFAGENTDESCR = "dfagentdescr"; // Number of registrations after the last lease-time-cleanup private int regsCnt = 0; private boolean tablesReady = false; /** * Default data type for very long strings */ protected String DEFAULT_LONGVARCHAR_TYPE = "LONGVARCHAR"; // This is used to generate unique IDs private String localIPAddress; private class PreparedStatements { // prepared SQL statements private PreparedStatement stm_selNrOfDescrForAID; private PreparedStatement stm_selAgentAddresses; private PreparedStatement stm_selAgentResolverAIDs; private PreparedStatement stm_selAgentUserDefSlot; private PreparedStatement stm_selLease; private PreparedStatement stm_selProtocols; private PreparedStatement stm_selLanguages; private PreparedStatement stm_selOntologies; private PreparedStatement stm_selServices; private PreparedStatement stm_selServiceProtocols; private PreparedStatement stm_selServiceLanguages; private PreparedStatement stm_selServiceOntologies; private PreparedStatement stm_selServiceProperties; private PreparedStatement stm_selExpiredDescr; private PreparedStatement stm_selSubscriptions; private PreparedStatement stm_selAllProtocols; private PreparedStatement stm_selCountAllProtocols; private PreparedStatement stm_selAllLanguages; private PreparedStatement stm_selCountAllLanguages; private PreparedStatement stm_selAllOntologies; private PreparedStatement stm_selCountAllOntologies; private PreparedStatement stm_insAgentDescr; private PreparedStatement stm_insAgentAddress; private PreparedStatement stm_insAgentUserDefSlot; private PreparedStatement stm_insAgentResolverAID; private PreparedStatement stm_insLanguage; private PreparedStatement stm_insOntology; private PreparedStatement stm_insProtocol; private PreparedStatement stm_insService; private PreparedStatement stm_insServiceProtocol; private PreparedStatement stm_insServiceOntology; private PreparedStatement stm_insServiceLanguage; private PreparedStatement stm_insServiceProperty; private PreparedStatement stm_insSubscription; private PreparedStatement stm_delAgentDescr; private PreparedStatement stm_delAgentUserDefSlot; private PreparedStatement stm_delAgentResolver; private PreparedStatement stm_delAgentAddress; private PreparedStatement stm_selDescrId; private PreparedStatement stm_selServiceId; private PreparedStatement stm_delService; private PreparedStatement stm_delLanguage; private PreparedStatement stm_delProtocol; private PreparedStatement stm_delOntology; private PreparedStatement stm_delServiceLanguage; private PreparedStatement stm_delServiceOntology; private PreparedStatement stm_delServiceProtocol; private PreparedStatement stm_delServiceProperty; private PreparedStatement stm_delSubscription; private PreparedStatements(Connection conn) throws SQLException { // select statements stm_selNrOfDescrForAID = conn.prepareStatement("SELECT COUNT(*) FROM dfagentdescr WHERE aid = ?"); stm_selAgentAddresses = conn.prepareStatement("SELECT address FROM agentaddress WHERE aid = ?"); stm_selAgentResolverAIDs = conn.prepareStatement("SELECT resolveraid FROM agentresolver WHERE aid = ?"); stm_selAgentUserDefSlot = conn.prepareStatement("SELECT slotkey, slotval FROM agentuserdefslot WHERE aid = ?"); stm_selLease = conn.prepareStatement("SELECT id, lease FROM dfagentdescr WHERE aid = ?"); stm_selProtocols = conn.prepareStatement("SELECT protocol FROM protocol WHERE descrid = ?"); stm_selLanguages = conn.prepareStatement("SELECT language FROM language WHERE descrid = ?"); stm_selOntologies = conn.prepareStatement("SELECT ontology FROM ontology WHERE descrid = ?"); stm_selServices = conn.prepareStatement("SELECT id, sname, stype, sownership FROM service WHERE descrid = ?"); stm_selServiceProtocols = conn.prepareStatement("SELECT protocol FROM serviceprotocol WHERE serviceid = ?"); stm_selServiceLanguages = conn.prepareStatement("SELECT ontology FROM serviceontology WHERE serviceid = ?"); stm_selServiceOntologies = conn.prepareStatement("SELECT language FROM servicelanguage WHERE serviceid = ?"); stm_selServiceProperties = conn.prepareStatement("SELECT propkey, propval_str, propval_obj FROM serviceproperty WHERE serviceid = ?"); stm_selDescrId = conn.prepareStatement("SELECT id FROM dfagentdescr WHERE aid = ?"); stm_selServiceId = conn.prepareStatement("SELECT id FROM service WHERE descrid = ?"); stm_selExpiredDescr = conn.prepareStatement("SELECT aid FROM dfagentdescr WHERE lease < ? AND lease <> '-1'"); stm_selSubscriptions = conn.prepareStatement("SELECT * FROM subscription"); stm_selAllProtocols = conn.prepareStatement("SELECT descrid, protocol FROM protocol ORDER BY descrid"); stm_selCountAllProtocols = conn.prepareStatement("SELECT COUNT(*) FROM protocol"); stm_selAllLanguages = conn.prepareStatement("SELECT descrid, language FROM language ORDER BY descrid"); stm_selCountAllLanguages = conn.prepareStatement("SELECT COUNT(*) FROM language"); stm_selAllOntologies = conn.prepareStatement("SELECT descrid, ontology FROM ontology ORDER BY descrid"); stm_selCountAllOntologies = conn.prepareStatement("SELECT COUNT(*) FROM ontology"); stm_insAgentDescr = conn.prepareStatement("INSERT INTO dfagentdescr VALUES (?, ?, ?)"); stm_insAgentAddress = conn.prepareStatement("INSERT INTO agentaddress VALUES (?, ?, ?)"); stm_insAgentUserDefSlot = conn.prepareStatement("INSERT INTO agentuserdefslot VALUES (?, ?, ?, ?)"); stm_insAgentResolverAID = conn.prepareStatement("INSERT INTO agentresolver VALUES (?, ?, ?)"); stm_insLanguage = conn.prepareStatement("INSERT INTO language VALUES (?, ?)"); stm_insOntology = conn.prepareStatement("INSERT INTO ontology VALUES (?, ?)"); stm_insProtocol = conn.prepareStatement("INSERT INTO protocol VALUES (?, ?)"); stm_insService = conn.prepareStatement("INSERT INTO service VALUES (?, ?, ?, ?, ?)"); stm_insServiceProtocol = conn.prepareStatement("INSERT INTO serviceprotocol VALUES (?, ?)"); stm_insServiceOntology = conn.prepareStatement("INSERT INTO serviceontology VALUES (?, ?)"); stm_insServiceLanguage = conn.prepareStatement("INSERT INTO servicelanguage VALUES (?, ?)"); stm_insServiceProperty = conn.prepareStatement("INSERT INTO serviceproperty VALUES (?, ?, ?, ?, ?)"); stm_insSubscription = conn.prepareStatement("INSERT INTO subscription VALUES (?, ?)"); // delete statements stm_delAgentDescr = conn.prepareStatement("DELETE FROM dfagentdescr WHERE id = ?"); stm_delAgentUserDefSlot = conn.prepareStatement("DELETE FROM agentuserdefslot WHERE aid = ?"); stm_delAgentResolver = conn.prepareStatement("DELETE FROM agentresolver WHERE aid = ?"); stm_delAgentAddress = conn.prepareStatement("DELETE FROM agentaddress WHERE aid = ?"); stm_delLanguage = conn.prepareStatement("DELETE FROM language WHERE descrid = ?"); stm_delProtocol = conn.prepareStatement("DELETE FROM protocol WHERE descrid = ?"); stm_delOntology = conn.prepareStatement("DELETE FROM ontology WHERE descrid = ?"); stm_delService = conn.prepareStatement("DELETE FROM service WHERE descrid = ?"); stm_delServiceLanguage = conn.prepareStatement("DELETE FROM servicelanguage WHERE serviceid = ?"); stm_delServiceOntology = conn.prepareStatement("DELETE FROM serviceontology WHERE serviceid = ?"); stm_delServiceProtocol = conn.prepareStatement("DELETE FROM serviceprotocol WHERE serviceid = ?"); stm_delServiceProperty = conn.prepareStatement("DELETE FROM serviceproperty WHERE serviceid = ?"); stm_delSubscription = conn.prepareStatement("DELETE FROM subscription WHERE id = ?"); } } /** * Constructor * @param maxResultLimit internal limit for the number of maximum search results. * @param drv database driver * @param url database URL * @param user database user name * @param passwd database password * @param cleanTables specifies whether the KB should delete all existing tables for the DF at startup * @throws SQLException an error occured while opening a connection to the database */ public DFDBKB(int maxResultLimit, String drv, String url, String user, String passwd, boolean cleanTables) throws SQLException { super(drv, url, user, passwd, maxResultLimit, cleanTables); } /** * Initializes all used SQL statements, the DB tables and the logging */ public void setup() throws SQLException { logger = Logger.getMyLogger(this.getClass().getName()); try { localIPAddress = InetAddress.getLocalHost().getHostAddress(); } catch (Exception e) { localIPAddress = "localhost"; } ConnectionWrapper wrapper = getConnectionWrapper(); Connection conn = wrapper.getConnection(); try { conn.setAutoCommit(false); // deactivate auto commit for better performance } catch (Exception e) { if(logger.isLoggable(Logger.WARNING)) { logger.log(Logger.WARNING, "Disabling auto-commit failed."); } } if (cleanTables) { // Drop all existing tables for the DF if required dropDFTables(); } createDFTables(); tablesReady = true; PreparedStatements ps = new PreparedStatements(conn); wrapper.setInfo(ps); clean(); } protected void initConnectionWrapper(ConnectionWrapper wrapper) throws SQLException { Connection conn = wrapper.getConnection(); try { conn.setAutoCommit(false); // deactivate auto commit for better performance } catch (Exception e) { if(logger.isLoggable(Logger.WARNING)) { logger.log(Logger.WARNING, "Disabling auto-commit failed."); } } // We cannot initialize the prepared-statements if the tables are not there yet if (tablesReady) { PreparedStatements ps = new PreparedStatements(conn); wrapper.setInfo(ps); } } private PreparedStatements getPreparedStatements() throws SQLException { ConnectionWrapper wrapper = getConnectionWrapper(); return (PreparedStatements)wrapper.getInfo(); } /** * Returns the name of the SQL type used in the * database to represent very long strings. */ protected String getLongVarCharType() { String bestMatch = DEFAULT_LONGVARCHAR_TYPE; try { // get the datatype with the highest precision from the meta data information DatabaseMetaData md = getConnectionWrapper().getConnection().getMetaData(); ResultSet typeInfo = md.getTypeInfo(); long maxPrecision = -1; while (typeInfo.next()) { long jdbcType = Long.parseLong(typeInfo.getString("DATA_TYPE")); long precision = Long.parseLong(typeInfo.getString("PRECISION")); if (jdbcType == Types.LONGVARCHAR && precision > maxPrecision) { maxPrecision = precision; bestMatch = typeInfo.getString("TYPE_NAME"); } } } catch (SQLException e) { // ignored --> default value will be returned } return bestMatch; } /** * Returns a global unique identifier */ protected String getGUID() { UID uid = new UID(); return localIPAddress + ":" + uid; } /** * Drops a DB table or does nothing if the table doesn't exist. */ protected void dropTable(Statement stmt, String tableName) { try { stmt.execute("DROP TABLE " + tableName + " CASCADE CONSTRAINTS"); getConnectionWrapper().getConnection().commit(); } catch (SQLException e) { // Check if the exception is because the table does not exist if (tableExists(tableName)) { logger.log(Logger.WARNING, "Cannot clean table "+tableName, e); } } } /** * Drops all existing DB tables used by the DF. */ protected void dropDFTables() throws SQLException { logger.log(Logger.INFO, "Cleaning DF tables..."); Statement stmt = getConnectionWrapper().getConnection().createStatement(); dropTable(stmt, SUBSCRIPTION); dropTable(stmt, SERVICEPROTOCOL); dropTable(stmt, SERVICEONTOLOGY); dropTable(stmt, SERVICELANGUAGE); dropTable(stmt, SERVICEPROPERTY); dropTable(stmt, SERVICE); dropTable(stmt, LANGUAGE); dropTable(stmt, ONTOLOGY); dropTable(stmt, PROTOCOL); dropTable(stmt, AGENTUSERDEFSLOT); dropTable(stmt, AGENTRESOLVER); dropTable(stmt, AGENTADDRESS); dropTable(stmt, DFAGENTDESCR); stmt.close(); } /** * Check whether a database table already exists * * @param name name of the table */ protected boolean tableExists(String name) { Statement stmt = null; try { stmt = getConnectionWrapper().getConnection().createStatement(); stmt.execute("SELECT COUNT(*) FROM "+name); return true; } catch (SQLException e) { // table doesn't exist return false; } finally { if (stmt != null) { try{ stmt.close(); } catch(SQLException see) { see.printStackTrace(); } } } } /** * Creates a new DB table * * @param name name of the table * @param entries array of column and constraint specifications * @throws SQLException If the table cannot be created */ protected void createTable(String name, String[] entries) { if (!tableExists(name)) { Statement stmt = null; try { Connection conn = getConnectionWrapper().getConnection(); stmt = conn.createStatement(); String sql = "CREATE TABLE " + name + " ("; for (int i = 0; i < entries.length; i++) { sql += entries[i]; if (i < entries.length - 1) sql += ", "; else sql += ")"; } stmt.executeUpdate(sql); conn.commit(); } catch (SQLException e) { if(logger.isLoggable(Logger.SEVERE)) logger.log(Logger.SEVERE, "Error creating table '"+name+"'", e); } finally { if (stmt != null) { try{ stmt.close(); } catch(SQLException see) { see.printStackTrace(); } } } } } /** * Adds explicit indices to the database to speed up queries */ protected void createIndices() { Statement stmt = null; try { Connection conn = getConnectionWrapper().getConnection(); stmt = conn.createStatement(); stmt.execute("CREATE INDEX dfagentDescrIdx ON dfagentdescr( aid )"); stmt.execute("CREATE INDEX leaseIdx ON dfagentdescr( lease )"); stmt.execute("CREATE INDEX agentAddressIdx ON agentaddress( aid )"); stmt.execute("CREATE INDEX agentResolverIdx ON agentresolver( aid )"); stmt.execute("CREATE INDEX agentUserdefslotIdx ON agentuserdefslot( aid )"); stmt.execute("CREATE INDEX serviceLanguageIdx ON servicelanguage( serviceid )"); stmt.execute("CREATE INDEX serviceProtocolIdx ON serviceprotocol( serviceid )"); stmt.execute("CREATE INDEX serviceOntologyIdx ON serviceontology( serviceid )"); stmt.execute("CREATE INDEX servicePropertyIdx ON serviceproperty( serviceid )"); stmt.execute("CREATE INDEX ontologyIdx ON ontology( descrid )"); stmt.execute("CREATE INDEX protocolIdx ON ontology( descrid )"); stmt.execute("CREATE INDEX languageIdx ON ontology( descrid )"); conn.commit(); } catch (SQLException e) { if(logger.isLoggable(Logger.FINE)) logger.log(Logger.FINE, "Indices for DF tables couldn't be created", e); } finally { if (stmt != null) { try{ stmt.close(); } catch(SQLException see) { see.printStackTrace(); } } } } /** * Creates the proper DB tables. */ protected void createDFTables() { String LONGVARCHAR_TYPE = getLongVarCharType(); // Tables for DF registrations createTable(DFAGENTDESCR, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")", "aid VARCHAR(" + MAX_PROP_LENGTH + ")", "lease VARCHAR(20)", "PRIMARY KEY( id )"}); createTable(AGENTADDRESS, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")", "aid VARCHAR(" + MAX_PROP_LENGTH + ")", "address VARCHAR(" + MAX_PROP_LENGTH + ")", "PRIMARY KEY( id )"}); createTable(AGENTRESOLVER, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")", "aid VARCHAR(" + MAX_PROP_LENGTH + ")", "resolveraid VARCHAR(" + MAX_PROP_LENGTH + ")", "PRIMARY KEY( id )"}); createTable(AGENTUSERDEFSLOT, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")", "aid VARCHAR(" + MAX_PROP_LENGTH + ")", "slotkey VARCHAR(" + MAX_PROP_LENGTH + ")", "slotval " + LONGVARCHAR_TYPE, "PRIMARY KEY( id )"}); createTable(ONTOLOGY, new String[] { "descrid VARCHAR(" + MAX_PROP_LENGTH + ")", "ontology VARCHAR(32)", "PRIMARY KEY( descrid, ontology )", "FOREIGN KEY( descrid ) REFERENCES dfagentdescr( id )" }); createTable(PROTOCOL, new String[] { "descrid VARCHAR(" + MAX_PROP_LENGTH + ")", "protocol VARCHAR(32)", "PRIMARY KEY( descrid, protocol )", "FOREIGN KEY( descrid ) REFERENCES dfagentdescr( id )" }); createTable(LANGUAGE, new String[] { "descrid VARCHAR(" + MAX_PROP_LENGTH + ")", "language VARCHAR(32)", "PRIMARY KEY( descrid, language )", "FOREIGN KEY( descrid ) REFERENCES dfagentdescr( id )" }); createTable(SERVICE, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")", "descrid VARCHAR(" + MAX_PROP_LENGTH + ")", "sname VARCHAR(" + MAX_PROP_LENGTH + ")", "stype VARCHAR(64)", "sownership VARCHAR(64)", "PRIMARY KEY( id )", "FOREIGN KEY( descrid ) REFERENCES dfagentdescr( id )" }); createTable(SERVICEPROTOCOL, new String[] { "serviceid VARCHAR(" + MAX_PROP_LENGTH + ")", "protocol VARCHAR(32)", "PRIMARY KEY( serviceid, protocol )", "FOREIGN KEY( serviceid ) REFERENCES service( id )"}); createTable(SERVICEONTOLOGY, new String[] { "serviceid VARCHAR(" + MAX_PROP_LENGTH + ")", "ontology VARCHAR(32)", "PRIMARY KEY( serviceid, ontology )", "FOREIGN KEY( serviceid ) REFERENCES service( id )"}); createTable(SERVICELANGUAGE, new String[] { "serviceid VARCHAR(" + MAX_PROP_LENGTH + ")", "language VARCHAR(32)", "PRIMARY KEY( serviceid, language )", "FOREIGN KEY( serviceid ) REFERENCES service( id )"}); createTable(SERVICEPROPERTY, new String[] { "serviceid VARCHAR(" + MAX_PROP_LENGTH + ")", "propkey VARCHAR(" + MAX_PROP_LENGTH + ")", "propval_obj " + LONGVARCHAR_TYPE, "propval_str VARCHAR(" + MAX_PROP_LENGTH + ")", "propvalhash VARCHAR(100)", "PRIMARY KEY( serviceid, propkey )", "FOREIGN KEY( serviceid ) REFERENCES service( id )"}); // Tables for subscriptions createTable(SUBSCRIPTION, new String[] { "id VARCHAR(" + MAX_PROP_LENGTH + ")" , "aclm " + LONGVARCHAR_TYPE, "PRIMARY KEY( id )"}); createIndices(); //DEBUG if(logger.isLoggable(Logger.FINE)) logger.log(Logger.FINE,"Tables correctly created"); } /** * Builds an error message for a <code>BatchUpdateException</code> */ private String getBatchUpdateErroMsg(BatchUpdateException e) { StringBuffer msg = new StringBuffer("SQLException: " + e.getMessage() + "\n"); msg.append("SQLState: " + e.getSQLState() + "\n"); msg.append("Message: " + e.getMessage() + "\n"); msg.append("Vendor: " + e.getErrorCode() + "\n"); msg.append("Update counts: "); int [] updateCounts = e.getUpdateCounts(); for (int i = 0; i < updateCounts.length; i++) { msg.append(updateCounts[i] + " "); } return msg.toString(); } /** * Stores the information of an AID of a resolver */ private void saveResolverAID(AID aid, AID resolverAid) throws SQLException { saveAID(resolverAid); PreparedStatements pss = getPreparedStatements(); pss.stm_insAgentResolverAID.setString(1, getGUID()); pss.stm_insAgentResolverAID.setString(2, aid.getName()); pss.stm_insAgentResolverAID.setString(3, resolverAid.getName()); pss.stm_insAgentResolverAID.addBatch(); } /** * Stores the information of an AID in the database */ private void saveAID(AID aid) throws SQLException { String name = aid.getName(); PreparedStatements pss = getPreparedStatements(); // Addresses Iterator iter = aid.getAllAddresses(); if (iter.hasNext()) { pss.stm_insAgentAddress.clearBatch(); while( iter.hasNext()){ pss.stm_insAgentAddress.setString(1, getGUID()); pss.stm_insAgentAddress.setString(2, name); pss.stm_insAgentAddress.setString(3, (String)iter.next()); pss.stm_insAgentAddress.addBatch(); } pss.stm_insAgentAddress.executeBatch(); } // User defined slots Properties props = aid.getAllUserDefinedSlot(); if (props.size() > 0) { pss.stm_insAgentUserDefSlot.clearBatch(); java.util.Iterator pIter = props.entrySet().iterator(); while (pIter.hasNext()) { Map.Entry entry = (Map.Entry)pIter.next(); pss.stm_insAgentUserDefSlot.setString(1, getGUID()); pss.stm_insAgentUserDefSlot.setString(2, name); pss.stm_insAgentUserDefSlot.setString(3, (String)entry.getKey()); pss.stm_insAgentUserDefSlot.setString(4, (String)entry.getValue()); pss.stm_insAgentUserDefSlot.addBatch(); } pss.stm_insAgentUserDefSlot.executeBatch(); } // Resolvers iter = aid.getAllResolvers(); if (iter.hasNext()) { pss.stm_insAgentResolverAID.clearBatch(); while(iter.hasNext()){ AID resolverAID = (AID)iter.next(); saveResolverAID(aid, resolverAID); } pss.stm_insAgentResolverAID.executeBatch(); } } /** * Returns all resolver AIDs for the given AID * @return <code>Collection</code> of aid strings */ private Collection getResolverAIDs(String aid) throws SQLException { ArrayList res = new ArrayList(); PreparedStatements pss = getPreparedStatements(); pss.stm_selAgentResolverAIDs.setString(1, aid); ResultSet rs = pss.stm_selAgentResolverAIDs.executeQuery(); while(rs.next()){ res.add(rs.getString(1)); } return res; } /** * Stores a collection of services for a specific description Id in the database * @param descrId id of the DFD these services belong to * @param iter iterator for a collection of <code>ServiceDescription</code> instances * @throws SQLException */ private void saveServices(String descrId, Iterator iter) throws SQLException { if (iter.hasNext()) { PreparedStatements pss = getPreparedStatements(); pss.stm_insService.clearBatch(); pss.stm_insServiceOntology.clearBatch(); pss.stm_insServiceOntology.clearBatch(); pss.stm_insServiceLanguage.clearBatch(); pss.stm_insServiceProperty.clearBatch(); boolean executeProtocolsBatch = false; boolean executeOntologiesBatch = false; boolean executeLanguagesBatch = false; boolean executePropertiesBatch = false; while(iter.hasNext()){ ServiceDescription service = (ServiceDescription)iter.next(); String serviceId = getGUID(); pss.stm_insService.clearParameters(); pss.stm_insService.setString(1, serviceId); pss.stm_insService.setString(2, descrId); pss.stm_insService.setString(3, service.getName()); pss.stm_insService.setString(4, service.getType()); pss.stm_insService.setString(5, service.getOwnership()); pss.stm_insService.addBatch(); // Service - Protocols Iterator iterS = service.getAllProtocols(); while(iterS.hasNext()){ pss.stm_insServiceProtocol.setString(1, serviceId); pss.stm_insServiceProtocol.setString(2, (String)iterS.next()); pss.stm_insServiceProtocol.addBatch(); executeProtocolsBatch = true; } // Service - Ontologies iterS = service.getAllOntologies(); while(iterS.hasNext()){ pss.stm_insServiceOntology.setString(1, serviceId); pss.stm_insServiceOntology.setString(2, (String)iterS.next()); pss.stm_insServiceOntology.addBatch(); executeOntologiesBatch = true; } // Service - Languages iterS = service.getAllLanguages(); while(iterS.hasNext()){ pss.stm_insServiceLanguage.setString(1, serviceId); pss.stm_insServiceLanguage.setString(2, (String)iterS.next()); pss.stm_insServiceLanguage.addBatch(); executeLanguagesBatch = true; } // Service - Properties iterS = service.getAllProperties(); while(iterS.hasNext()){ Property prop = (Property)iterS.next(); try { pss.stm_insServiceProperty.setString(1, serviceId); pss.stm_insServiceProperty.setString(2, prop.getName()); // serialize value to a string and calcualte // a hash map for later search operations Object value = prop.getValue(); // store plain String object value directly // in 'propval_str' field otherwise store it in // 'propval_obj' field and fill hash field (this will be used in search phase to allow matching Serializable objects) if ( needSerialization(value) ) { //System.out.println("DF Handling Object property "+prop.getName()+": value = "+value); String valueStr = serializeObj(value); pss.stm_insServiceProperty.setString(3, valueStr); pss.stm_insServiceProperty.setString(4, null); String hashStr = getHashValue(value); pss.stm_insServiceProperty.setString(5, hashStr); } else { // set to NULL the serialized representation of the object and its hash //System.out.println("DF Handling String property "+prop.getName()+": value = "+value); pss.stm_insServiceProperty.setString(3, null); pss.stm_insServiceProperty.setString(4, (String) value); pss.stm_insServiceProperty.setString(5, null); }; pss.stm_insServiceProperty.addBatch(); executePropertiesBatch = true; } catch (Exception e) { if(logger.isLoggable(Logger.SEVERE)) logger.log(Logger.SEVERE,"Cannot serialize property '" + prop.getName() + "' for service '" + service.getName() + "'", e); } } } pss.stm_insService.executeBatch(); if (executeProtocolsBatch) { pss.stm_insServiceProtocol.executeBatch(); } if (executeOntologiesBatch) { pss.stm_insServiceOntology.executeBatch(); } if (executeLanguagesBatch) { pss.stm_insServiceLanguage.executeBatch(); } if (executePropertiesBatch) { pss.stm_insServiceProperty.executeBatch(); } } } private static final boolean needSerialization(Object value) { return !((value instanceof String) && ( ((String) value).length() <= MAX_PROP_LENGTH )); } /** * Insert a new DFD object. * @return the previous DFD (if any) corresponding to the same AID */ protected Object insertSingle(Object name, Object fact) throws SQLException { DFAgentDescription dfd = (DFAgentDescription) fact; AID agentAID = dfd.getName(); String agentName = agentAID.getName(); DFAgentDescription dfdToReturn = null; String batchErrMsg = ""; Connection conn = getConnectionWrapper().getConnection(); PreparedStatements pss = getPreparedStatements(); try { // -- Remove the previous DFD if any dfdToReturn = (DFAgentDescription) removeSingle(dfd.getName()); // -- add new DFD // DF Agent Description Date leaseTime = dfd.getLeaseTime(); long lt = (leaseTime != null ? leaseTime.getTime() : -1); String descrId = getGUID(); pss.stm_insAgentDescr.setString(1, descrId); pss.stm_insAgentDescr.setString(2, agentName); pss.stm_insAgentDescr.setString(3, String.valueOf(lt)); pss.stm_insAgentDescr.executeUpdate(); // AID saveAID(agentAID); // Languages Iterator iter = dfd.getAllLanguages(); if (iter.hasNext()) { pss.stm_insLanguage.clearBatch(); while(iter.hasNext()){ pss.stm_insLanguage.setString(1, descrId); pss.stm_insLanguage.setString(2, (String)iter.next()); pss.stm_insLanguage.addBatch(); } pss.stm_insLanguage.executeBatch(); } // Ontologies iter = dfd.getAllOntologies(); if (iter.hasNext()) { pss.stm_insOntology.clearBatch(); while(iter.hasNext()){ pss.stm_insOntology.setString(1, descrId); pss.stm_insOntology.setString(2, (String)iter.next()); pss.stm_insOntology.addBatch(); } pss.stm_insOntology.executeBatch(); } // Protocols iter = dfd.getAllProtocols(); if (iter.hasNext()) { pss.stm_insProtocol.clearBatch(); while(iter.hasNext()){ pss.stm_insProtocol.setString(1, descrId); pss.stm_insProtocol.setString(2, (String)iter.next()); pss.stm_insProtocol.addBatch(); } pss.stm_insProtocol.executeBatch(); } // Services saveServices(descrId, dfd.getAllServices()); regsCnt++; // clear outdated entries after a certain number of new registrations if(regsCnt > MAX_REGISTER_WITHOUT_CLEAN){ regsCnt = 0; clean(); } conn.commit(); } catch (SQLException sqle) { // Rollback the transaction try { conn.rollback(); } catch (SQLException se) { logger.log(Logger.SEVERE,"Rollback for incomplete insertion of DFD for agent "+dfd.getName() + " failed.", se); } // Re-throw the exception throw sqle; } return dfdToReturn; } /** * Remove the DFD object corresponding to the indicated AID. * @return the removed DFD (if any) */ protected Object removeSingle(Object name) throws SQLException { AID agentAID = (AID) name; String n = agentAID.getName(); DFAgentDescription dfd = getDFD(n); if (dfd != null) { remove(n); } return dfd; } /** * Retrieve the DFDs matching the given template */ protected List searchSingle(Object template, int maxResult) throws SQLException { List matchingAIDs = new ArrayList(); // Get the names of all DFDs matching the template String select = null; ResultSet rs = null; Statement s = null; try { select = createSelect((DFAgentDescription) template); s = getConnectionWrapper().getConnection().createStatement(); if (maxResult >= 0) { s.setMaxRows(maxResult); s.setFetchSize(maxResult); } rs = s.executeQuery(select); while(rs.next()) { String aidS = rs.getString("aid"); matchingAIDs.add(aidS); } } catch(SQLException sqle) { // Let it through throw sqle; } catch(Exception e) { logger.log(Logger.SEVERE, "Couldn't create the SQL SELECT statement.", e); throw new SQLException("Couldn't create the SQL SELECT statement. "+e.getMessage()); } finally { closeResultSet(rs); closeStatement(s); } // For each matching AID reconstruct the complete DFD List dfds = new ArrayList(matchingAIDs.size()); Iterator it = matchingAIDs.iterator(); // FIXME: Define a proper constant and possibly a proper configuration option if (matchingAIDs.size() < 10) { while (it.hasNext()) { dfds.add(getDFD((String) it.next())); } } else { // If we found several matching agents we preload protocols languages and ontologies once for all // instead of making several queries one per agent. PreparedStatements pss = getPreparedStatements(); Map allLanguages = preloadIdValueTable(pss.stm_selCountAllLanguages, pss.stm_selAllLanguages); Map allOntologies = preloadIdValueTable(pss.stm_selCountAllOntologies, pss.stm_selAllOntologies); Map allProtocols = preloadIdValueTable(pss.stm_selCountAllProtocols, pss.stm_selAllProtocols); while (it.hasNext()) { dfds.add(getDFD((String) it.next(), allLanguages, allOntologies, allProtocols)); } } return dfds; } private Map preloadIdValueTable(PreparedStatement cntStm, PreparedStatement stm) throws SQLException { Map m = null; ResultSet rs = cntStm.executeQuery(); rs.next(); long recordCount = rs.getLong(1); closeResultSet(rs); if (recordCount < MAX_PRELOAD_CNT) { // If there are more than MAX_PRELOAD_CNT elements return null. In fact it is more time consuming constructing the preload Map // than performing all queries in the DB. rs = stm.executeQuery(); if (true) { m = new HashMap(); String currentId = null; List l = null; while(rs.next()){ String id = rs.getString(1); // id (using the index is faster) if (!id.equals(currentId)) { l = new ArrayList(); m.put(id, l); currentId = id; } l.add(rs.getString(2)); // value (protocol, language, ontology ... depending on the passed statement) } closeResultSet(rs); } } return m; } /** */ protected KBIterator iteratorSingle(Object template) throws SQLException { String select = null; ResultSet rs = null; Statement s = null; try { select = createSelect((DFAgentDescription) template); s = getConnectionWrapper().getConnection().createStatement(); rs = s.executeQuery(select); return new DFDBKBIterator(s, rs); } catch(SQLException se){ logger.log(Logger.SEVERE, "Error accessing DB: "+select, se); closeResultSet(rs); closeStatement(s); throw se; } catch(Exception e) { logger.log(Logger.SEVERE, "Error creating SQL SELECT statement.", e); throw new SQLException("Error creating SQL SELECT statement. "+e.getMessage()); } } /** Inner class DFDBKBIterator */ private class DFDBKBIterator implements KBIterator { private Statement s = null; private ResultSet rs = null; private boolean hasMoreElements = false; public DFDBKBIterator(Statement s, ResultSet rs) throws SQLException { this.s = s; this.rs = rs; if (rs != null) { // Move to the first row hasMoreElements = rs.next(); } } public boolean hasNext() { return hasMoreElements; } public Object next() { if (hasMoreElements) { try { String name = rs.getString("aid"); DFAgentDescription dfd = getDFD(name); hasMoreElements = rs.next(); return dfd; } catch (SQLException sqle) { hasMoreElements = false; throw new NoSuchElementException("DB Error. "+sqle.getMessage()); } } throw new NoSuchElementException(""); } public void remove() { // Not implemented } public void close() { closeResultSet(rs); closeStatement(s); } } // END of inner class DFDBKBIterator /** * Reconstructs an AID object corresponding to the given AID name * @throws SQLException */ private AID getAID(String aidN) throws SQLException { ResultSet rs = null; AID id = new AID(aidN, AID.ISGUID); PreparedStatements pss = getPreparedStatements(); // AID addresses pss.stm_selAgentAddresses.setString(1, aidN); rs = pss.stm_selAgentAddresses.executeQuery(); while(rs.next()){ id.addAddresses(rs.getString(1)); } // AID resolvers Collection resolvers = getResolverAIDs(aidN); Iterator iter = resolvers.iterator(); while (iter.hasNext()) { id.addResolvers(getAID((String)iter.next())); } // AID User defined slots pss.stm_selAgentUserDefSlot.setString(1, aidN); rs = pss.stm_selAgentUserDefSlot.executeQuery(); while(rs.next()) { String key = rs.getString("slotkey"); String value = rs.getString("slotval"); id.addUserDefinedSlot(key, value); } return id; } private DFAgentDescription getDFD(String aidN) throws SQLException { return getDFD(aidN, null, null, null); } /** Reconstruct the DFD corresponding to the given AID name (if any) */ private DFAgentDescription getDFD(String aidN, Map allLanguages, Map allOntologies, Map allProtocols) throws SQLException { DFAgentDescription dfd = null; AID id = null; ResultSet rs = null; ResultSet rsS = null; String descrId = null; try{ PreparedStatements pss = getPreparedStatements(); // Check if there is a DFD corresponding to aidN and get lease time pss.stm_selLease.setString(1, aidN); rs = pss.stm_selLease.executeQuery(); if (rs.next()) { dfd = new DFAgentDescription(); id = getAID(aidN); dfd.setName(id); String sLease = rs.getString("lease"); descrId = rs.getString("id"); long lease = Long.parseLong(sLease); if (lease != -1) { dfd.setLeaseTime(new Date(lease)); } } else { return null; } closeResultSet(rs); // Protocols loadProtocols(descrId, dfd, allProtocols); // Languages loadLanguages(descrId, dfd, allLanguages); // Ontologies loadOntologies(descrId, dfd, allOntologies); // Services pss.stm_selServices.setString(1, descrId); rs = pss.stm_selServices.executeQuery(); while(rs.next()) { ServiceDescription sd = new ServiceDescription(); String serviceId = rs.getString("id"); sd.setName(rs.getString("sname")); sd.setType(rs.getString("stype")); sd.setOwnership(rs.getString("sownership")); // Service protocols pss.stm_selServiceProtocols.setString(1, serviceId); rsS = pss.stm_selServiceProtocols.executeQuery(); while(rsS.next()){ sd.addProtocols(rsS.getString(PROTOCOL)); } closeResultSet(rsS); // Service languages pss.stm_selServiceLanguages.setString(1, serviceId); rsS = pss.stm_selServiceLanguages.executeQuery(); while(rsS.next()){ sd.addOntologies(rsS.getString(ONTOLOGY)); } closeResultSet(rsS); // Service ontologies pss.stm_selServiceOntologies.setString(1, serviceId); rsS = pss.stm_selServiceOntologies.executeQuery(); while(rsS.next()){ sd.addLanguages(rsS.getString(LANGUAGE)); } closeResultSet(rsS); // Service properties pss.stm_selServiceProperties.setString(1, serviceId); rsS = pss.stm_selServiceProperties.executeQuery(); while(rsS.next()){ Property prop = new Property(); prop.setName(rsS.getString("propkey")); String objStrVal = rsS.getString("propval_obj"); String strStrVal = rsS.getString("propval_str"); Object value = ( objStrVal == null )? strStrVal : deserializeObj(objStrVal); prop.setValue(value); sd.addProperties(prop); } dfd.addServices(sd); } } catch (SQLException sqle) { // Let it through throw sqle; } catch (Exception e) { logger.log(Logger.SEVERE, "Unexpected error retrieving DFD for agent "+aidN, e); throw new SQLException("Unexpected error retrieving DFD for agent "+aidN+". "+e.getMessage()); } finally { closeResultSet(rs); closeResultSet(rsS); } return dfd; } private final void loadOntologies(String descrId, DFAgentDescription dfd, Map allOntologies) throws SQLException { if (allOntologies != null) { List ontos = (List) allOntologies.get(descrId); if (ontos != null) { Iterator it = ontos.iterator(); while (it.hasNext()) { dfd.addOntologies((String) it.next()); } } } else { PreparedStatements pss = getPreparedStatements(); pss.stm_selOntologies.setString(1, descrId); ResultSet rs = pss.stm_selOntologies.executeQuery(); while(rs.next()){ dfd.addOntologies(rs.getString(ONTOLOGY)); } closeResultSet(rs); } } private final void loadLanguages(String descrId, DFAgentDescription dfd, Map allLanguages) throws SQLException { if (allLanguages != null) { List langs = (List) allLanguages.get(descrId); if (langs != null) { Iterator it = langs.iterator(); while (it.hasNext()) { dfd.addLanguages((String) it.next()); } } } else { PreparedStatements pss = getPreparedStatements(); pss.stm_selLanguages.setString(1, descrId); ResultSet rs = pss.stm_selLanguages.executeQuery(); while(rs.next()){ dfd.addLanguages(rs.getString(LANGUAGE)); } closeResultSet(rs); } } private final void loadProtocols(String descrId, DFAgentDescription dfd, Map allProtocols) throws SQLException { if (allProtocols != null) { List protos = (List) allProtocols.get(descrId); if (protos != null) { Iterator it = protos.iterator(); while (it.hasNext()) { dfd.addProtocols((String) it.next()); } } } else { PreparedStatements pss = getPreparedStatements(); pss.stm_selProtocols.setString(1, descrId); ResultSet rs = pss.stm_selProtocols.executeQuery(); while(rs.next()){ dfd.addProtocols(rs.getString(PROTOCOL)); } closeResultSet(rs); } } /** * Deletes the AID corresponding to the indicated agent name * including all its resolver AIDs (if there are no df descriptions left for them) */ private void removeAID(String aid) throws SQLException { PreparedStatements pss = getPreparedStatements(); // check whether there exists a DF description for the agent pss.stm_selNrOfDescrForAID.setString(1, aid); ResultSet rs = pss.stm_selNrOfDescrForAID.executeQuery(); int found = 0; if (rs.next()) found = Integer.parseInt(rs.getString(1)); // no description found --> delete if (found == 0) { // user definded slots pss.stm_delAgentUserDefSlot.setString(1, aid); pss.stm_delAgentUserDefSlot.execute(); // resolvers Collection resolverAIDs = getResolverAIDs(aid); Iterator iter = resolverAIDs.iterator(); while (iter.hasNext()) { removeAID((String)iter.next()); } pss.stm_delAgentResolver.setString(1, aid); pss.stm_delAgentResolver.execute(); // address pss.stm_delAgentAddress.setString(1, aid); pss.stm_delAgentAddress.execute(); } } /** * Deletes all services corresponding to the indicated description ID * @throws SQLException */ private void removeServices(String descrId) throws SQLException { ResultSet rs = null; PreparedStatements pss = getPreparedStatements(); pss.stm_selServiceId.setString(1, descrId); rs = pss.stm_selServiceId.executeQuery(); boolean executeBatch = false; while (rs.next()) { String serviceId = rs.getString("id"); pss.stm_delServiceLanguage.setString(1, serviceId); pss.stm_delServiceLanguage.addBatch(); pss.stm_delServiceOntology.setString(1, serviceId); pss.stm_delServiceOntology.addBatch(); pss.stm_delServiceProtocol.setString(1, serviceId); pss.stm_delServiceProtocol.addBatch(); pss.stm_delServiceProperty.setString(1, serviceId); pss.stm_delServiceProperty.addBatch(); pss.stm_delService.setString(1, descrId); pss.stm_delService.addBatch(); executeBatch = true; } rs.close(); if (executeBatch) { pss.stm_delServiceLanguage.executeBatch(); pss.stm_delServiceOntology.executeBatch(); pss.stm_delServiceProtocol.executeBatch(); pss.stm_delServiceProperty.executeBatch(); pss.stm_delService.executeBatch(); } } /** * Delete the DFD object corresponding to the indicated agent name. */ private void remove(String aid) throws SQLException { ResultSet rs = null; Connection conn = getConnectionWrapper().getConnection(); try { PreparedStatements pss = getPreparedStatements(); // get description ID pss.stm_selDescrId.setString(1, aid); rs = pss.stm_selDescrId.executeQuery(); if (rs.next()) { String descrId = rs.getString("id"); closeResultSet(rs); // ontologies pss.stm_delOntology.setString(1, descrId); pss.stm_delOntology.execute(); // protocols pss.stm_delProtocol.setString(1, descrId); pss.stm_delProtocol.execute(); // languages pss.stm_delLanguage.setString(1, descrId); pss.stm_delLanguage.execute(); // services removeServices(descrId); // agent description pss.stm_delAgentDescr.setString(1, descrId); pss.stm_delAgentDescr.execute(); // AID removeAID(aid); conn.commit(); } else { if(logger.isLoggable(Logger.FINE)) logger.log(Logger.FINE,"No DF description found to remove for agent '"+aid+"'"); } } catch(SQLException sqle){ try { conn.rollback(); } catch (SQLException se) { logger.log(Logger.SEVERE,"Rollback for incomplete remotion of DFD for agent "+aid + " failed.", se); } throw sqle; } finally { closeResultSet(rs); } } /** * Convert a template DFAgentDescription into the SQL SELECT * operation that returns the AID names whose DFAgentDescriptions * match the given template. */ private String createSelect(DFAgentDescription dfdTemplate) throws Exception { StringBuffer select = new StringBuffer("SELECT dfagentdescr.aid FROM dfagentdescr"); List lAs = new ArrayList(); List lWhere = new ArrayList(); // Name AID agentAID = dfdTemplate.getName(); if(agentAID != null){ lWhere.add(" dfagentdescr.aid = '"+agentAID.getName()+"'"); } // Lease time Date lease = dfdTemplate.getLeaseTime(); long currTime = System.currentTimeMillis(); lWhere.add(" (dfagentdescr.lease = '-1' OR dfagentdescr.lease > '"+currTime+"')"); // Languages Iterator iter = dfdTemplate.getAllLanguages(); int i=0; while(iter.hasNext()){ String tmp = LANGUAGE+i; lAs.add(", language "+tmp); lWhere.add(tmp+".language='"+(String)iter.next()+"'"); lWhere.add(tmp+".descrid=dfagentdescr.id"); i++; } // Ontologies iter = dfdTemplate.getAllOntologies(); i = 0; while(iter.hasNext()){ String tmp = ONTOLOGY+i; lAs.add(", ontology "+tmp); lWhere.add(tmp+".ontology='"+(String)iter.next()+"'"); lWhere.add(tmp+".descrid=dfagentdescr.id"); i++; } // Protocols iter = dfdTemplate.getAllProtocols(); i = 0; while(iter.hasNext()){ String tmp = PROTOCOL+i; lAs.add(", protocol "+tmp); lWhere.add(tmp+".protocol='"+(String)iter.next()+"'"); lWhere.add(tmp+".descrid=dfagentdescr.id"); i++; } // Services iter = dfdTemplate.getAllServices(); i = 0; while(iter.hasNext()){ ServiceDescription service = (ServiceDescription)iter.next(); String serviceName = service.getName(); String serviceType = service.getType(); String serviceOwner = service.getOwnership(); // Service name, type and ownership String tmp = SERVICE+i; lAs.add(", service "+tmp); if(serviceName != null){ lWhere.add(tmp+".sname='"+serviceName+"'"); } if(serviceType != null){ lWhere.add(tmp+".stype='"+serviceType+"'"); } if(serviceOwner != null){ lWhere.add(tmp+".sownership='"+serviceOwner+"'"); } lWhere.add(tmp+".descrid=dfagentdescr.id"); i++; // Service languages Iterator iterS = service.getAllLanguages(); int j = 0; while(iterS.hasNext()){ String tmp1 = SERVICELANGUAGE+j; lAs.add(", servicelanguage "+tmp1); lWhere.add(tmp1+".language='"+(String)iterS.next()+"'"); lWhere.add(tmp1+".serviceid="+tmp+".id"); j++; } // Service ontologies iterS = service.getAllOntologies(); j = 0; while(iterS.hasNext()){ String tmp1 = SERVICEONTOLOGY+j; lAs.add(", serviceontology "+tmp1); lWhere.add(tmp1+".ontology='"+(String)iterS.next()+"'"); lWhere.add(tmp1+".serviceid="+tmp+".id"); j++; } // Service protocols iterS = service.getAllProtocols(); j = 0; while(iterS.hasNext()){ String tmp1 = SERVICEPROTOCOL+j; lAs.add(", serviceprotocol "+tmp1); lWhere.add(tmp1+".protocol='"+(String)iterS.next()+"'"); lWhere.add(tmp1+".serviceid="+tmp+".id"); j++; } // Service properties iterS = service.getAllProperties(); j = 0; while(iterS.hasNext()){ String tmp1 = SERVICEPROPERTY+j; lAs.add(", serviceproperty "+tmp1); Property prop = (Property) iterS.next(); if (prop.getName() != null) lWhere.add(tmp1+".propkey='"+prop.getName()+"'"); Object value = prop.getValue(); if (value != null) { if (needSerialization(value)) { String hashStr = getHashValue(prop.getValue()); lWhere.add(tmp1+".propvalhash='"+ hashStr +"'"); } else { lWhere.add(tmp1+".propval_str='"+ value +"'"); } } lWhere.add(tmp1+".serviceid="+tmp+".id"); j++; } } // Concatenate all the aliases iter = lAs.iterator(); while (iter.hasNext()) { select.append((String) iter.next()); } // Concatenate all WHERE if (lWhere.size() > 0) { select.append(" WHERE "); } iter = lWhere.iterator(); i = 0; while (iter.hasNext()) { if(i > 0) { select.append(" and "); } select.append((String) iter.next()); ++i; } return select.toString(); } //////////////////////////////////////// // DB cleaning methods //////////////////////////////////////// /** Removes DF registrations and subscriptions whose lease time has expired. This method is called at startup and each MAX_REGISTER_WITHOUT_CLEAN registrations. */ private void clean(){ cleanExpiredRegistrations(); cleanExpiredSubscriptions(); } /** * Removes DF registrations whose lease time has expired. */ private void cleanExpiredRegistrations(){ ResultSet rs = null; long currTime = System.currentTimeMillis(); try{ PreparedStatements pss = getPreparedStatements(); pss.stm_selExpiredDescr.setString(1, String.valueOf(currTime)); rs = pss.stm_selExpiredDescr.executeQuery(); while(rs.next()){ remove(rs.getString("aid")); } } catch(SQLException se){ if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING, "Error cleaning expired DF registrations", se); } finally { closeResultSet(rs); } } /** Removes DF subscriptions whose lease time has expired. */ private void cleanExpiredSubscriptions() { //FIXME: To be implemented } private StringACLCodec codec = new StringACLCodec(); protected void subscribeSingle(Object dfd, SubscriptionResponder.Subscription s) throws SQLException, NotUnderstoodException{ ACLMessage aclM = s.getMessage(); String msgStr = aclM.toString(); String convID = aclM.getConversationId(); registerSubscription(convID, msgStr); } /** * Add a subscription to the database * @param convID conversation id (used as primary key) * @param aclM ACL message for the subscription */ private void registerSubscription(String convID, String aclM) throws SQLException { Connection conn = getConnectionWrapper().getConnection(); try { PreparedStatements pss = getPreparedStatements(); String base64Str = new String(Base64.encodeBase64(aclM.getBytes("US-ASCII")), "US-ASCII"); // --> convert string to Base64 encoding pss.stm_insSubscription.setString(1, convID); pss.stm_insSubscription.setString(2, base64Str); pss.stm_insSubscription.execute(); conn.commit(); } catch (SQLException sqle) { // Rollback the transaction try { conn.rollback(); } catch (SQLException se) { logger.log(Logger.SEVERE,"Rollback for incomplete subscription failed.", se); } // Re-throw the exception throw sqle; } catch (Exception e) { logger.log(Logger.SEVERE, "Error encoding subscription message in Base64.", e); throw new SQLException("Error encoding subscription message in Base64. "+e.getMessage()); } } /** * Return all known subscriptions at the DF * @return <code>Enumeration</code> with instances of the class * <code> jade.proto.SubscriptionResponder&Subscription</code> */ public Enumeration getSubscriptions(){ Vector subscriptions = new Vector(); StringACLCodec codec = new StringACLCodec(); ResultSet rs = null; try { rs = getPreparedStatements().stm_selSubscriptions.executeQuery(); while (rs.next()) { String base64Str = rs.getString("aclm"); String aclmStr = new String(Base64.decodeBase64(base64Str.getBytes("US-ASCII")), "US-ASCII"); ACLMessage aclm = codec.decode(aclmStr.getBytes(), ACLCodec.DEFAULT_CHARSET); subscriptions.add(sr.createSubscription(aclm)); } } catch (Exception e) { if(logger.isLoggable(Logger.SEVERE)) logger.log(Logger.SEVERE, "Error retrieving subscriptions from the database", e); } finally { closeResultSet(rs); } return subscriptions.elements(); } protected void unsubscribeSingle(SubscriptionResponder.Subscription sub) throws SQLException { ACLMessage aclM = sub.getMessage(); String convID = aclM.getConversationId(); boolean deleted = deregisterSubscription(convID); if(!deleted) if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"No subscription to delete."); } /** * Removes a registration from the database. * @param convID id for the subscription * @return <code>true</code> if an entry has been found and removed * - otherwise <code>false</code> */ private boolean deregisterSubscription(String convID) throws SQLException { Connection conn = getConnectionWrapper().getConnection(); try { PreparedStatements pss = getPreparedStatements(); pss.stm_delSubscription.setString(1, convID); int rowCount = pss.stm_delSubscription.executeUpdate(); conn.commit(); return (rowCount != 0); } catch (SQLException sqle) { // Rollback the transaction try { conn.rollback(); } catch (SQLException se) { logger.log(Logger.SEVERE,"Rollback for incomplete un-subscription failed.", se); } // Re-throw the exception throw sqle; } } //////////////////////////////////////////////// // Helper methods //////////////////////////////////////////////// /** * Closes an open result set and logs an appropriate error message * when it fails */ private void closeResultSet(ResultSet rs) { try { if (rs != null) { rs.close(); rs = null; } } catch (SQLException e) { // result set has already been closed. //(depends party on the database driver) } } /** * Closes an open SQL statement and logs an appropriate error message * when it fails */ private void closeStatement(Statement s) { try { if (s != null) { s.close(); s = null; } } catch (Exception e) { if(logger.isLoggable(Logger.WARNING)) logger.log(Logger.WARNING,"Closing SQL statement failed."); } } /** * Serializes any serializable object to a Base64 encoded string * @param obj the object to serialize * @throws IOException An error during the serialization */ private String serializeObj(Object obj) throws IOException { if (obj == null) return null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close(); byte[] data = baos.toByteArray(); return new String(Base64.encodeBase64(data), "US-ASCII"); } /** * Deserializes any serializable object from a string * @param str string which represents a serialized object * @throws IOException An error during the serialization * @throws ClassNotFoundException The deserialized java class is unknown */ private Object deserializeObj(String str) throws IOException, ClassNotFoundException { if (str == null) return null; byte[] data = Base64.decodeBase64(str.getBytes("US-ASCII")); ByteArrayInputStream bais = new ByteArrayInputStream(data); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } /** * Returns an MD5 hash value for an object * @param obj Object to use for the hash value calcualtion * @return an MD5 value encoded in ISO-8859-1 * @throws Exception The hash value couldn't be generated */ protected String getHashValue(Object obj) throws Exception { final String HASH_ALGORITHM = "MD5"; if (obj == null) return "null"; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(obj); oos.close(); byte[] data = baos.toByteArray(); MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM); byte[] digest = md.digest(data); return new String(Base64.encodeBase64(digest), "US-ASCII"); } catch (Exception e) { throw new Exception("Couldn't create " + HASH_ALGORITHM + " hash for given object.", e); } } /** * This method must be used when inserting text values into a DB. It doubles all ' and " characters * If the passed parameter is null, then an empty string is returned. **/ protected String prepDBStr(String s) { if (s == null){ return ""; } String result = replace(s,"'","''"); result = replace(result,"\"","\"\"" ); return result; } /** * Replaces all occurences of a <code>pattern</code> in <code>str</code> * with <code>replaceWith</code> * @param str source string * @param pattern pattern string to search for * @param replaceWith new string */ protected String replace(String str, String pattern, String replaceWith) { int s = 0; int e = 0; StringBuffer result = new StringBuffer(); while ((e = str.indexOf(pattern, s)) >= 0) { result.append(str.substring(s, e)); if(replaceWith != null) result.append(replaceWith); s = e+pattern.length(); } result.append(str.substring(s)); return result.toString(); } }