/**
* SLPv2 DA Database Management
*
* We implemented the database function within Java2. We didn't use
* a standard DBMS for simplicity and efficiency. The whole database
* is organized as red-black tree (provided by Java)
*
* (1) the key is ltag+URL, assume each service has a unique URL,
* and the same service can be registered using different
* languaage (ltag)
* (2) the value at each node is an Entry class, which keeps all the
* information about the service.
*
*/
package eu.artemis.shield.discovery.slpdaemon.impl;
import java.io.*;
import java.util.*;
import java.security.interfaces.*;
public class Database {
public static final int DELETED = 0;
public static final int LANGUAGE_TAG = 1;
public static final int NAMING_AUTHORITY =2;
public static final int TYPE = 3;
public static final int URL = 4;
public static final int LIFE_TIME = 5;
public static final int SCOPE = 6;
public static final int VERSION_TS = 7; // version TS from the SA for an update
public static final int ARRIVAL_TS = 8; // arrival TS at the DA for an update
public static final int ACCEPT_DA = 9; // the accept DA for the update
public static final int ACCEPT_TS = 10; // the accept TS for the update
public static final int ATTRIBUTES = 11;
da daf;
TreeMap table;
slpMsgComposer composer;
Vector matchedEntry;
ByteArrayOutputStream b;
DataOutputStream d;
int totalMatch,i=0;
public Database(da daf) {
this.daf = daf;
table = new TreeMap();
composer = new slpMsgComposer();
matchedEntry = new Vector(10);
b = new ByteArrayOutputStream();
d = new DataOutputStream(b);
}
// return the number of entries in the database
public int size() {
return table.size();
}
public TreeMap table() {
return table;
}
//------------------------------------------------
// save database to either the stdout or the file
//------------------------------------------------
public void saveDatabase(BufferedWriter o)
{
Iterator values = table.values().iterator();
while (values.hasNext())
{
Entry e = (Entry) values.next();
e.prtEntry(/*daf, */o);
}
}
//------------------------------------------------
// return the database as Vector of Vector
//------------------------------------------------
public Vector getDatabase()
{
Vector db = new Vector();
Vector row;
Iterator values = table.values().iterator();
while (values.hasNext())
{
row = new Vector();
Entry e = (Entry) values.next();
row.add(new String("" + e.getDeleted()));
row.add(e.getLtag());
row.add(e.getType());
row.add(e.getNA());
row.add(e.getURL());
row.add(new Long(e.getLifetime())); //int
row.add(e.getScope());
row.add(new Long(e.getVersionTS())); //long
row.add(new Long(e.getArrivalTS())); //long
row.add(e.getAcceptDA());
row.add(new Long(e.getAcceptTS())); //long
row.add(e.getAttr(""));
db.add(row);
}
return db;
}
//------------------------------------------
// load data form file to internal database
//------------------------------------------
public void loadDatabase(String dbase) {
String line, ltag, type, url, scope, acceptDA, attr = "";
int lifetime;
long versionTS, arrivalTS, acceptTS;
boolean deleted = false;
try {
BufferedReader in =
new BufferedReader(new FileReader(dbase));
while ((line = in.readLine()) != null) {
StringTokenizer st = new StringTokenizer(line);
deleted = Boolean.valueOf(st.nextToken()).booleanValue();
ltag = st.nextToken();
type = st.nextToken();
url = st.nextToken();
lifetime = Integer.parseInt(st.nextToken());
scope = st.nextToken();
versionTS = Long.parseLong(st.nextToken());
arrivalTS = Long.parseLong(st.nextToken());
acceptDA = st.nextToken();
acceptTS = Long.parseLong(st.nextToken());
// Now the DA registers all the values of a given attribute
st.nextToken(",");
StringBuffer SB = new StringBuffer(2048);
while (st.hasMoreTokens()){
SB.append(st.nextToken(","));
SB.append(",");
}
attr = SB.toString();
addEntry(deleted, ltag, type, url, lifetime, scope, attr,
Const.fresh_flag, versionTS, arrivalTS,
acceptDA, acceptTS);
}
in.close();
} catch (Exception e) {
da.appendDebug("Database::loadDatabase");
da.appendDebug(e);
}
daf.refreshDatabaseTable();
}
//-------------------------------------------
// check lifetime and remove expired entries
//-------------------------------------------
public void rmExpiredEntries() {
boolean dbChanged = false;
long currtime = System.currentTimeMillis();
Iterator keys = table.keySet().iterator();
while (keys.hasNext()) {
String k = (String) keys.next();
Entry e = (Entry) table.get(k);
int lif = e.getLifetime() *1000;
if ((lif>0) && (currtime > (e.getArrivalTS() + e.getLifetime()*1000))) {
dbChanged = true;
keys.remove();
}
}
if (dbChanged) daf.refreshDatabaseTable();
}
//---------------------------------------------------------
// for "SrvReg"
// add a new entry of service registration to the database
// or replace/update its previous registration
//---------------------------------------------------------
public int addEntry(boolean deleted, String ltag, String type,
String url, int lifetime, String scope, String attr, int reg_flag,
long versionTS, long arrivalTS, String acceptDA, long acceptTS) {
if (Const.DEBUG_MATCHING_ENABLED)
{
da.appendDebug("Database::addEntry");
da.appendDebug("\t"+deleted+"\t"+ltag+"\t"+type+"\t"+url);
da.appendDebug("\t"+lifetime+"\t"+scope+"\t"+attr+"\t"+reg_flag+"\n\n");
}
if (!table.containsKey(ltag+url)) {
if ((reg_flag & Const.fresh_flag) == 0) { // incremental SrvReg
return Const.INVALID_UPDATE;
}
Entry ent = new Entry();
table.put(ltag+url, ent);
}
Entry e = (Entry) table.get(ltag+url);
int result = e.update(deleted, ltag, url, type, lifetime, scope, attr,
reg_flag, versionTS, arrivalTS, acceptDA, acceptTS);
daf.refreshDatabaseTable();
return result;
}
//---------------------------------------------------
// for "SrvDeReg"
// remove the entry with the key: ltag+url (tag=="")
// or delete some attributes of this entry (tag!="")
//---------------------------------------------------
public int rmEntry(String ltag, String url, String scope, String tag, long versionTS, String acceptDA, long acceptTS)
{
if (table.containsKey(ltag+url))
{
Entry e = (Entry) table.get(ltag+url);
if (!scope.equalsIgnoreCase(e.getScope()))
{
return Const.SCOPE_NOT_SUPPORTED;
}
e.deletion(tag, versionTS, acceptDA, acceptTS);
}
daf.refreshDatabaseTable();
return Const.OK;
}
//----------------------------------------------------------------------
// for "SrvTypeRqst"
// get the list of service types for specified scope & naming authority
//----------------------------------------------------------------------
public String getServiceTypeList(String na, String scope)
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug(
"\nSrvTypeRqst" +
"\n-----------" +
"\nDatabase::getServiceTypeList(Naming Authority,Scope)" +
"\n- Naming Authority = "+ na +
"\n- Scope = "+ scope);
Vector typelist = new Vector();
Iterator values = table.values().iterator();
while (values.hasNext())
{
Entry e = (Entry) values.next();
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Analysing database service --> " + e.getURL());
if (!e.getDeleted()) // entry not deleted
{
if (scope.equalsIgnoreCase(e.getScope())) // match scope
{
if (na.equals("*") || na.equals("") || na.equalsIgnoreCase(e.getNA()))
{
if (!typelist.contains(e.getType())) // has not been already listed
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("> OKAY --> Service type " + e.getType() + " added to the list\n");
typelist.addElement(e.getType());
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("> DISCARDED --> This service type has been already added to the list\n");
}
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("> DISCARDED --> Different naming authority (" + e.getNA() + " vs " + na + ")\n");
}
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("> DISCARDED --> Different scope (" + e.getScope() + " vs " + scope + ")\n");
}
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("> DISCARDED --> Service has been deleted\n");
}
}
StringBuffer tl = new StringBuffer();
for (int i=0; i<typelist.size(); i++)
{
String s = (String)typelist.elementAt(i);
if (tl.length() > 0) tl.append(",");
tl.append(s);
}
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("\n- Returned Service Type List = " + tl.toString() + "\n");
return tl.toString();
}
//-----------------------------------------------------------
// for "SrvRqst"
// find the matched URLs with (type, scope, predicate, ltag)
// return: error code (short)
// number of matched URLs (short)
// URL blocks (decided by previous #URL)
//-----------------------------------------------------------
public int getTotalMatch() {
return totalMatch;
}
public Vector getMatchedEntry() {
return matchedEntry;
}
public byte[] getMatchedURL(String type, String scope,
String pred, String ltag, Vector ssExtList, int ecode,int ID, RSAPublicKey publicKey) {
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("\n*** SrvRqst ***" +
"\nDatabase::getMatchedURL(Type,Scope,Predicate,Ltag)" +
"\n- Type = "+ type +
"\n- Scope = "+ scope +
"\n- Predicate = "+ pred +
"\n- Ltag = "+ ltag+"\n");
byte[] buf = null;
// if scope == "" it means it's the default scope.
if (scope == "") scope = Const.defaultScope;
if (!Util.shareString(daf.getScope(), scope, ","))
{
ecode = Const.SCOPE_NOT_SUPPORTED;
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getMatchedURL() -> SCOPE NOT SUPPORTED!");
}
// obtain matched entries
matchedEntry.clear();
Iterator values = table.values().iterator();
while (values.hasNext())
{
Entry e = (Entry) values.next();
if (e.match(type, scope, pred, ltag, (ecode!=Const.AUTHENTICATION_ABSENT)&&(ecode!=Const.AUTHENTICATION_FAILED)&& ID!=Const.SrvRqst))
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getMatchedURL() -> Service " + e.getURL() + " ADDED\n");
matchedEntry.addElement(e);
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getMatchedURL() -> Service " + e.getURL() + " FILTERED\n");
}
}
totalMatch = matchedEntry.size();
if (Const.DEBUG_MATCHING_ENABLED)
{
da.appendDebug("Database::getMatchedURL() -> Filtering Process Terminated.");
da.appendDebug("Database::getMatchedURL() -> " + totalMatch + " services found.");
}
// filter matched entries
for (int i=0; i<ssExtList.size(); i++) {
SelectSortExt ss = (SelectSortExt) ssExtList.elementAt(i);
if (ss.getID() == Const.SelectExt) {
int bound = ss.getBound();
if (bound < matchedEntry.size()) matchedEntry.setSize(bound);
} else if (ss.getID() == Const.SortExt) {
sortFilter(ss.getKey());
}
}
// write matched URLs to buffer
b.reset();
try {
d.writeShort(ecode); // error code
d.writeShort(matchedEntry.size()); // URL count
// fill in matched URLs
for (int i=0; i<matchedEntry.size(); i++) {
Entry e = (Entry) matchedEntry.elementAt(i);
d.writeByte(0);
d.writeShort(e.getLifetime());
if (ID==Const.SrvRqstAuth)
{
d.writeShort(e.getCryptoURL(publicKey).length);
d.write(e.getCryptoURL(publicKey));
}
else
{
d.writeShort(e.getURL().length());
d.writeBytes(e.getURL());
}
d.writeByte(0);
}
buf = b.toByteArray();
} catch (Exception ex) {
da.appendDebug("Databse::getMatchedURL");
da.appendDebug(ex);
}
return buf;
}
private void sortFilter(String key) {
int size = matchedEntry.size();
if (size <= 1) return; // no need to sort
StringTokenizer st = new StringTokenizer(key, ",");
int nkey = st.countTokens();
String[] keys = new String[nkey];
int[] types = new int[nkey];
int[] orders = new int[nkey];
Integer[] vals = new Integer[nkey];
for (int i=0; i<nkey; i++) {
String unit = st.nextToken();
StringTokenizer st1 = new StringTokenizer(unit, ":");
if (st1.countTokens() < 3) {
da.append("Incorrect sort key list");
}
keys[i] = st1.nextToken().trim(); // key
String s = st1.nextToken().trim(); // type
if (s.equalsIgnoreCase("s")) {
types[i] = Const.StringSort;
} else {
types[i] = Const.IntegerSort;
}
s = st1.nextToken().trim(); // order
if (s.equals("+")) {
orders[i] = Const.IncreasingOrder;
} else {
orders[i] = Const.DecreasingOrder;
}
if (st1.hasMoreTokens()) { // reference value
vals[i] = new Integer(st1.nextToken().trim());
} else {
vals[i] = null;
}
}
Vector se = new Vector(size);
for (int i=0; i<size; i++) {
Entry e = (Entry)matchedEntry.elementAt(i);
se.addElement(new SortEntry(e, keys, types, orders, vals));
}
Collections.sort(se);
matchedEntry.clear();
for (int i=0; i<size; i++) {
SortEntry s = (SortEntry) se.elementAt(i);
matchedEntry.addElement(s.getEntry());
}
}
//------------------------------------------------------
/**
* @author Vincenzo Suraci
*
* @param URLorType: is a ServiceURL or a Service Type
* @param scope: is a comma separated scope list
* @param tag: is a comma separated attribute tag list
* @param ltag: is a language tag
*
* @return a String containing all the attribute tags that match with the Service URL or Type,
* with the scope list, with the attribute tag list and with the language tag
*/
//------------------------------------------------------
public String getAttrList(String URLOrType, String scope, String tag, String ltag)
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("\n\nDatabase::getAttrList(url, scope, tag, ltag)" +
"\n- URL or Service Type = "+ URLOrType +
"\n- Scope List = "+ scope +
"\n- Attribute Tag List = " + tag +
"\n- Language Tag = " + ltag + "\n");
/*
* Check if we have a ServiceURL or a ServiceTpye
*/
String url = URLOrType;
if (table.containsKey(ltag+url))
{
//if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> We have a URL!");
Entry e = (Entry) table.get(ltag+url);
if (e.getDeleted())
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " has been DELETED");
return "";
}
else if (e.isExpired())
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " is EXPIRED");
return "";
}
else if (!Util.containsString(scope, e.getScope(), ","))
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " has a different scope (" + e.getScope() + " is not contined in " + scope + ")");
return "";
}
return e.getAttr(tag);
}
String result = "";
String type = URLOrType;
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> We have a TYPE!");
/*
* FROM RFC 2614
* For the type and scope, return a Vector of all ServiceLocationAttribute objects whose ids match the String
* patterns in the attributeIds Vector regardless of the Locator's locale. The request is made independent of
* language locale. If no attributes are found, an empty vector is returned.
*/
Iterator it = table.keySet().iterator();
while (it.hasNext())
{
Entry e = (Entry)table.get(it.next());
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Evaluating Service " + e.getURL());
if (e.getDeleted())
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " has been DELETED");
return "";
}
else if (e.isExpired())
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " is EXPIRED");
return "";
}
else if (type.equalsIgnoreCase(e.getType()))
{
if (!Util.containsString(scope, e.getScope(), ","))
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " has a different scope (" + e.getScope() + " is not contined in " + scope + ")");
return "";
}
if (result.equals("")) result = e.getAttr(tag);
else result += "," + e.getAttr(tag);
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> adding these attributes: " + e.getAttr(tag));
}
else
{
if (Const.DEBUG_MATCHING_ENABLED) da.appendDebug("Database::getAttrList() -> Service " + e.getURL() + " has different type (\"" + e.getType() + "\" differes from \"" + type + "\")");
}
}
return result;
}
/*
private String typeAttrList(String type, String scope,
String tag, String ltag) {
StringBuffer attrList = new StringBuffer();
Iterator values = table.values().iterator();
while (values.hasNext()) {
Entry e = (Entry) values.next();
if (Const.DEBUG_ENABLED)
{
da.append("DEBUG->Database::typeAttrList(...) Found a entry:");
da.append("entry.type="+e.getType()+"?=?"+type);
da.append("entry.scope="+e.getScope()+"?=?"+scope);
da.append("entry.ltag="+e.getLtag()+"?=?"+ltag);
da.append("entry:deleted="+e.getDeleted());
da.append("entry:attrs="+e.getAttr(tag));
}
if (!e.getDeleted() &&
type.equalsIgnoreCase(e.getType()) &&
scope.equalsIgnoreCase(e.getScope()) &&
ltag.equalsIgnoreCase(e.getLtag())) {
String s = e.getAttr(tag);
if (attrList.length() > 0) attrList.append(",");
attrList.append(s);
}
}
return attrList.toString();
}
*/
//------------------------------------------------------------------
// find new states based on selective/complete & (rdaList, rtsList)
// sort new states on their accept IDs and return in TreeMap
//------------------------------------------------------------------
public Vector findNewStates(String rscope,
Vector rdaList, Vector rtsList, int etrpType) {
Vector tmp = new Vector();
Iterator values = table.values().iterator();
while (values.hasNext()) {
Entry e = (Entry) values.next();
String ada = e.getAcceptDA();
long ats = e.getAcceptTS();
int index = rdaList.indexOf(ada); // a requested subset?
long rts = 0;
if (index != -1) { // it is a requested subset, find rts
rts = ((Long) rtsList.get(index)).longValue();
}
if (Util.shareString(rscope, e.getScope(), ",")) {
if (index != -1 && ats > rts ||
index == -1 && etrpType == Const.complete) {
tmp.addElement(e);
}
}
}
Collections.sort(tmp);
return tmp;
}
public String toString(){
String t ="\n\n----- Database Contents -----\n\n";
Iterator i = table.values().iterator();
int n =1;
while (i.hasNext()){
t += "\nEntry "+n+")\n"+((Entry)i.next()).toString();
}
return t;
}
}
|