/**
* 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();
}
}