/*
* This file or a portion of this file is licensed under the terms of
* the Globus Toolkit Public License, found in file ../GTPL, or at
* http://www.globus.org/toolkit/download/license.html. This notice must
* appear in redistributions of this file, with or without modification.
*
* Redistributions of this Software, with or without modification, must
* reproduce the GTPL in: (1) the Software, or (2) the Documentation or
* some other similar material which is provided with the Software (if
* any).
*
* Copyright 1999-2004 University of Chicago and The University of
* Southern California. All rights reserved.
*/
package org.griphyn.vdl.dbschema;
import java.sql.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;
import java.io.*;
import java.lang.reflect.*;
import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;
import javax.xml.transform.OutputKeys;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.DOMException;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.InputSource;
import org.griphyn.vdl.util.ChimeraProperties;
import org.griphyn.vdl.classes.*;
import org.griphyn.vdl.util.Logging;
import org.griphyn.vdl.parser.*;
import org.griphyn.vdl.router.Cache;
import org.griphyn.vdl.annotation.*;
import edu.isi.pegasus.common.util.Separator;
import org.xml.sax.InputSource;
/**
* This class provides basic functionalities to interact with the
* backend database, such as insertion, deletion, and search of
* entities in the VDC.
*
* @author Jens-S. Vöckler
* @author Yong Zhao
* @version $Revision$
*/
public class NXDSchema extends DatabaseSchema implements XDC
{
/**
* An instance of the VDLx XML parser.
*/
private org.griphyn.vdl.parser.VDLxParser m_parser;
private DocumentBuilderFactory m_factory;
private DocumentBuilder m_builder;
/**
* reference to collection '/db/vdc';
*/
protected Collection m_db;
protected Collection m_vdc;
protected Collection m_meta;
protected CollectionManagementService m_dbColService;
protected CollectionManagementService m_vdcColService;
protected XPathQueryService m_dbQrySvc;
protected XPathQueryService m_vdcQrySvc;
protected XPathQueryService m_metaQrySvc;
protected XUpdateQueryService m_xupdQrySvc;
/**
* A cache for definitions to avoid reloading from the database.
*/
protected Cache m_cache;
/**
* Instantiates an XML parser for VDLx on demand. Since XML parsing
* and parser instantiation is an expensive business, the reader will
* only be generated on demand.
*
* @return a valid VDLx parser instance.
*/
private org.griphyn.vdl.parser.VDLxParser parserInstance()
{
if ( this.m_parser == null ) {
// obtain the schema location URL from the schema properties:
// url is a list of strings representing schema locations. The
// content exists in pairs, one of the namespace URI, one of the
// location URL.
String url = null;
try {
ChimeraProperties props = ChimeraProperties.instance();
url =
m_dbschemaprops.getProperty( "xml.url", props.getVDLSchemaLocation() );
} catch (IOException e) {
Logging.instance().log("nxd", 0, "ignored " + e);
}
this.m_parser = new org.griphyn.vdl.parser.VDLxParser(url);
}
// done
return this.m_parser;
}
/**
* Default constructor for the NXD schema.
*/
public NXDSchema(String dbDriver)
throws ClassNotFoundException,
NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException,
SQLException, IOException,ParserConfigurationException
{
// load the driver from the properties
super(); // call minimalistic c'tor, no driver loading!
ChimeraProperties props = ChimeraProperties.instance();
m_dbschemaprops =
props.getDatabaseSchemaProperties( PROPERTY_PREFIX );
m_cache = null;
m_parser = null;
// extract those properties specific to the database driver.
// use default settings.
String driverPrefix = null;
String driverName = props.getDatabaseDriverName(driverPrefix);
Properties driverprops = props.getDatabaseDriverProperties(driverPrefix);
String url = props.getDatabaseURL(driverPrefix);
String user = driverprops.getProperty("user", "guest");
String passwd = driverprops.getProperty("password", "guest");
try {
m_factory = DocumentBuilderFactory.newInstance();
m_builder = m_factory.newDocumentBuilder();
Class cl = Class.forName(driverName);
Database database = (Database) cl.newInstance();
DatabaseManager.registerDatabase(database);
// get the collection
m_db = DatabaseManager.getCollection(url + "/db", user, passwd);
m_dbColService = (CollectionManagementService)
m_db.getService("CollectionManagementService", "1.0");
m_vdc = m_db.getChildCollection("vdc");
if(m_vdc == null) {
// collection does not exist, create
m_vdc = m_dbColService.createCollection("vdc");
}
m_vdc.setProperty(OutputKeys.INDENT, "no");
m_meta = m_db.getChildCollection("metadata");
if(m_meta == null) {
// collection does not exist, create
m_meta = m_dbColService.createCollection("metadata");
}
m_meta.setProperty(OutputKeys.INDENT, "no");
m_vdcColService = (CollectionManagementService)
m_vdc.getService("CollectionManagementService", "1.0");
m_dbQrySvc = (XPathQueryService) m_db.getService("XPathQueryService", "1.0");
m_vdcQrySvc = (XPathQueryService) m_vdc.getService("XPathQueryService", "1.0");
m_metaQrySvc = (XPathQueryService) m_meta.getService("XPathQueryService", "1.0");
m_dbQrySvc.setProperty("indent", "no");
m_vdcQrySvc.setProperty("indent", "no");
m_metaQrySvc.setProperty("indent", "no");
XUpdateQueryService m_xupdQrySvc = (XUpdateQueryService) m_meta.getService("XUpdateQueryService", "1.0");
} catch (XMLDBException e) {
throw new SQLException (e.getMessage());
}
}
private String getDefinitionId(Definition def) {
String prefix = (def.getType() == Definition.TRANSFORMATION)?"TR_":"DV_";
String version = def.getVersion();
String suffix = "";
if (version != null)
suffix = "_" + version;
return prefix + def.getName() + suffix;
}
private String getDefinitionId(String name,
String version,
int type) {
String prefix = (type == Definition.TRANSFORMATION)?"TR_":"DV_";
String suffix = "";
if (version != null)
suffix = "_" + version;
return prefix + name + suffix;
}
//
// lower level methods, working directly on specific definitions
//
/**
* Loads a single Definition from the backend database into an Java object.
* This method does not allow wildcarding!
*
* @param namespace namespace, null will be converted into empty string
* @param name name, null will be converted into empty string
* @param version version, null will be converted into empty string
* @param type type of the definition (TR or DV), must not be -1.
* @return the Definition as specified, or null if not found.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
* @see #saveDefinition( Definition, boolean )
*/
public Definition
loadDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
Definition result = null;
Logging.instance().log("xaction", 1, "START load definition" );
try {
Collection col;
if (namespace != null)
col = m_vdc.getChildCollection(namespace);
else
col = m_vdc;
String id = getDefinitionId(name, version, type);
//Logging.instance().log( "nxd", 0, "Definition id " + id );
XMLResource res = (XMLResource)col.getResource(id);
if(res != null) {
MyCallbackHandler cb = new MyCallbackHandler();
parserInstance().parse( new org.xml.sax.InputSource(new StringReader((String)res.getContent())), cb );
result = cb.getDefinition();
} else {
Logging.instance().log( "nxd", 0, "Definition not found" );
}
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
Logging.instance().log("xaction", 1, "FINAL load definition" );
return result;
}
/**
* Saves a Definition, that is either a Transformation or Derivation,
* into the backend database. This method, of course, does not allow
* wildcarding. The definition has to be completely specified and
* valid.
*
* @param definition is the new Definition to store.
* @param overwrite true, if existing defitions will be overwritten by
* new ones with the same primary (or secondary) key (-set), or false,
* if a new definition will be rejected on key matches.
*
* @return true, if the backend database was changed, or
* false, if the definition was not accepted into the backend.
*
* @see org.griphyn.vdl.classes.Definition
* @see org.griphyn.vdl.classes.Transformation
* @see org.griphyn.vdl.classes.Derivation
* @see #loadDefinition( String, String, String, int )
*/
public boolean
saveDefinition( Definition definition,
boolean overwrite )
throws SQLException
{
Logging.instance().log( "nxd", 2, "START save definition");
try {
String namespace = definition.getNamespace();
Collection col;
if (namespace != null)
col = m_vdc.getChildCollection(namespace);
else
col = m_vdc;
String id = getDefinitionId(definition);
if (col == null) {
// collection does not exist, create
col = m_vdcColService.createCollection(namespace);
} else if (!overwrite) {
if (col.getResource(id) != null){
Logging.instance().log( "app", 0, definition.shortID() +
" already exists, ignoring" );
return false;
}
}
// create new XMLResource; an id will be assigned to the new resource
XMLResource document = (XMLResource)col.createResource(id, "XMLResource");
document.setContent(definition.toXML("", null));
col.storeResource(document);
// add to cache
//if ( m_cache != null ) m_cache.set( new Long(id), definition );
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
// done
Logging.instance().log( "nxd", 2, "FINAL save definition");
return true;
}
/**
* Search the database for the existence of a definition.
*
* @param definition the definition object to search for
* @return true, if the definition exists, false if not found
*/
public boolean containsDefinition( Definition definition )
throws SQLException
{
boolean result = false;
try {
String namespace = definition.getNamespace();
Collection col;
if (namespace != null)
col = m_vdc.getChildCollection(namespace);
else
col = m_vdc;
String id = getDefinitionId(definition);
if(col != null) {
if (col.getResource(id) != null){
result = true;
}
}
} catch ( Exception e ) {
throw new SQLException(e.getMessage());
}
return result;
}
/**
* Delete a specific Definition objects from the database. No wildcard
* matching will be done. "Fake" definitions are permissable, meaning
* it just has the secondary key triple.
*
* @param definition is the definition specification to delete
* @return true is something was deleted, false if non existent.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public boolean deleteDefinition( Definition definition )
throws SQLException
{
boolean result = false;
Logging.instance().log("xaction", 1, "START delete definition" );
try {
String namespace = definition.getNamespace();
Collection col;
if (namespace != null)
col = m_vdc.getChildCollection(namespace);
else
col = m_vdc;
String id = getDefinitionId(definition);
if(col != null) {
XMLResource res = (XMLResource)col.getResource(id);
if (res != null){
col.removeResource(res);
result = true;
}
}
} catch ( Exception e ) {
// ignore
}
Logging.instance().log("xaction", 1, "FINAL delete definition" );
return result;
}
/**
* Delete Definition objects from the database. This method allows for
* wildcards in the usual fashion. Use null for strings as wildcards,
* and -1 for the type wildcard.
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type definition type (TR or DV)
* @return a list containing all Definitions that were deleted
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List deleteDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
Logging.instance().log("xaction", 1, "START delete definitions" );
java.util.List result = searchDefinition(namespace, name, version, type);
for (int i=0; i < result.size(); i++)
deleteDefinition((Definition)result.get(i));
Logging.instance().log("xaction", 1, "FINAL delete definitions" );
return result;
}
/**
* Search the database for definitions by ns::name:version triple
* and by type (either Transformation or Derivation). This version
* of the search allows for jokers expressed as null value
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type type of definition (TR/DV, or both)
* @return a list of definitions
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List searchDefinition( String namespace,
String name,
String version,
int type )
throws SQLException
{
String xquery = ""; //"declare namespace vdl='http://www.griphyn.org/chimera/VDL';"
String triple = "";
if (namespace != null)
triple += "[@namespace='" + namespace + "']";
if (name != null)
triple += "[@name='" + name + "']";
if (version != null)
triple += "[@version='" + version + "']";
if (type != -1) {
if (type == Definition.TRANSFORMATION)
xquery = "//transformation" + triple;
else
xquery += "//derivation" + triple;
} else
xquery = "//derivation" + triple + "|//transformation" + triple;
return searchDefinition(xquery);
}
/**
* Searches the database for all derivations that satisfies a certain query.
*
* @param xquery the query statement
* @return a list of definitions
*/
public java.util.List searchDefinition( String xquery )
throws SQLException
{
if ( xquery == null )
throw new NullPointerException("You must specify a query!");
java.util.List result = new ArrayList();
try {
Logging.instance().log("xaction", 1, "query: " + xquery );
ResourceSet rs = m_dbQrySvc.query(xquery);
ResourceIterator i = rs.getIterator();
while(i.hasMoreResources()) {
Resource res = i.nextResource();
MyCallbackHandler cb = new MyCallbackHandler();
parserInstance().parse( new org.xml.sax.InputSource(new StringReader((String)res.getContent())), cb );
result.add(cb.getDefinition());
}
} catch ( Exception e ) {
throw new SQLException(e.getMessage());
}
return result;
}
/**
* Searches the database for elements that satisfies a certain query.
*
* @param xquery the query statement
* @return a list of string
*/
public java.util.List searchElements( String xquery )
throws SQLException
{
if ( xquery == null )
throw new NullPointerException("You must specify a query!");
java.util.List result = new ArrayList();
try {
Logging.instance().log("nxd", 1, "query: " + xquery );
ResourceSet rs = m_dbQrySvc.query(xquery);
ResourceIterator i = rs.getIterator();
while(i.hasMoreResources()) {
Resource res = i.nextResource();
result.add((String)res.getContent());
}
} catch ( Exception e ) {
throw new SQLException(e.getMessage());
}
return result;
}
/**
* Searches the database for annotations that satisfies a certain query.
*
* @param xquery the query statement
* @return true if found, false otherwise
*/
public XMLResource findAnnotation( String xquery )
throws SQLException
{
if ( xquery == null )
return null;
try {
Logging.instance().log("nxd", 1, "query: " + xquery );
ResourceSet rs = m_metaQrySvc.query(xquery);
ResourceIterator i = rs.getIterator();
if (i.hasMoreResources()) {
XMLResource res = (XMLResource)i.nextResource();
return res;
} else
return null;
} catch ( Exception e ) {
throw new SQLException(e.getMessage());
}
}
public java.util.List searchFilename( String lfn, int link )
throws SQLException
{
if ( lfn == null )
throw new NullPointerException("You must query for a filename");
String linkQuery = "";
String type = LFN.toString(link);
if (type != null)
linkQuery = "[@link = '" + type + "']";
String xquery =
"//derivation[.//lfn[@file = '" + lfn + "']" + linkQuery + "]";
java.util.List result = searchDefinition(xquery);
Logging.instance().log("xaction", 1, "FINAL select LFNs" );
return result;
}
/**
* Delete one or more definitions from the backend database. The key
* triple parameters may be wildcards. Wildcards are expressed as
* <code>null</code> value, or have regular expression.
*
* @param namespace namespace
* @param name name
* @param version version
* @param type definition type (TR or DV)
* @return a list of definitions that were deleted.
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List
deleteDefinitionEx( String namespace,
String name,
String version,
int type )
throws SQLException {
Logging.instance().log("xaction", 1, "START delete definitions ex" );
java.util.List result = searchDefinitionEx(namespace, name, version, type);
for (int i=0; i < result.size(); i++)
deleteDefinition((Definition)result.get(i));
Logging.instance().log("xaction", 1, "FINAL delete definitions ex" );
return result;
}
/**
* Searches the database for definitions by ns::name:version triple
* and by type (either Transformation or Derivation). This version of
* the search allows for jokers expressed as null value
*
* @param namespace namespace, null to match any namespace
* @param name name, null to match any name
* @param version version, null to match any version
* @param type type of definition, see below, or -1 as wildcard
* @return a list of Definition items, which may be empty
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
* @see #loadDefinition( String, String, String, int )
*/
public java.util.List
searchDefinitionEx( String namespace,
String name,
String version,
int type )
throws SQLException {
String xquery = "";
String triple = "";
if (namespace != null)
triple += "[matches(@namespace, '" + namespace + "')]";
if (name != null)
triple += "[matches(@name, '" + name + "')]";
if (version != null)
triple += "[matches(@version, '" + version + "')]";
if (type != -1) {
if (type == Definition.TRANSFORMATION)
xquery = "//transformation" + triple;
else
xquery += "//derivation" + triple;
} else
xquery = "//derivation" + triple + "|//transformation" + triple;
return searchDefinition(xquery);
}
/**
* Searches the database for all LFNs that match a certain pattern.
* The linkage is an additional constraint. This method allows
* regular expression
*
* @param lfn the LFN name
* @param link the linkage type of the LFN
* @return a list of filenames that match the criterion.
*
* @see org.griphyn.vdl.classes.LFN#NONE
* @see org.griphyn.vdl.classes.LFN#INPUT
* @see org.griphyn.vdl.classes.LFN#OUTPUT
* @see org.griphyn.vdl.classes.LFN#INOUT
*/
public java.util.List
searchLFN( String lfn, int link )
throws SQLException {
if ( lfn == null )
throw new NullPointerException("You must query for a filename");
String linkQuery = "";
String type = LFN.toString(link);
if (type != null)
linkQuery = "[@link = '" + type + "')]";
String xquery =
// "//lfn[matches(@file, '" + lfn + "')]" + linkQuery + "/@file";
"//lfn" + linkQuery + "/@file[matches(., '" + lfn + "')]";
java.util.List result = searchElements(xquery);
Logging.instance().log("xaction", 1, "FINAL select LFNs" );
return result;
}
/**
* Searches the database for a list of namespaces of the definitions
* Sorted in ascending order.
*
* @param type type of definition, see below, or -1 for both
* @return a list of namespaces
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List
getNamespaceList( int type )
throws SQLException {
String xquery = "";
if (type == Definition.TRANSFORMATION)
xquery = "for $n in distinct-values(//transformation/@namespace) order by $n return $n";
else if (type == Definition.DERIVATION)
xquery = "for $n in distinct-values(//derivation/@namespace) order by $n return $n";
else
xquery = "for $n in distinct-values(//derivation/@namespace|//transformation/@namespace) order by $n return $n";
java.util.List result = searchElements(xquery);
Logging.instance().log("xaction", 1, "FINAL select LFNs" );
return result;
}
/**
* Searches the database for a list of fully-qualified names of
* the definitions sorted in ascending order.
*
* @param type type of definition, see below, or -1 for both.
* @return a list of FQDNs
*
* @see org.griphyn.vdl.classes.Definition#TRANSFORMATION
* @see org.griphyn.vdl.classes.Definition#DERIVATION
*/
public java.util.List
getFQDNList( int type )
throws SQLException {
String xquery = "";
if (type == Definition.TRANSFORMATION)
xquery = "for $d in //transformation order by $d/@namespace empty least, $d/@name, $d/@version return string-join((string-join(($d/@namespace, $d/@name), '::'), $d/@version), ':')";
else if (type == Definition.DERIVATION)
xquery = "for $d in //derivation order by $d/@namespace empty least, $d/@name, $d/@version return string-join((string-join(($d/@namespace, $d/@name), '::'), $d/@version), ':')";
else
xquery = "for $d in (//transformation|//derivation) order by $d/@namespace empty least, $d/@name, $d/@version return string-join((string-join(($d/@namespace, $d/@name), '::'), $d/@version), ':')";
java.util.List result = searchElements(xquery);
Logging.instance().log("xaction", 1, "FINAL select LFNs" );
return result;
}
/**
* Deletes an annotation with the specified key.
*
* @param primary is the primary object specifier for the class.
* According to the type, this is either the FQDI, or the filename.
* @param secondary is a helper argument for annotations to calls
* and formal arguments, and should be null for all other classes.
* For calls, the argument must be packed into {@link java.lang.Integer}.
* @param kind defines the kind/class of object to annotate.
* @param key is the annotation key.
* @return true, if the database was modified, false otherwise.
* @exception SQLException, if something went wrong during database
* access.
*/
public boolean deleteAnnotation( String primary, Object secondary,
int kind, String key )
throws SQLException, IllegalArgumentException
{
String subject = "";
String select = null;
switch ( kind ) {
case CLASS_TRANSFORMATION:
subject = "tr";
break;
case CLASS_DERIVATION:
subject = "dv";
break;
case CLASS_CALL:
// may throw ClassCastException
subject = "tr";
select = "call[" + ((Integer) secondary).intValue() + "]";
break;
case CLASS_DECLARE:
subject = "tr";
// may throw ClassCastException
//select = "declare[@name='" + (String)secondary + "']";
select = (String)secondary;
break;
case CLASS_FILENAME:
subject = "lfn";
break;
default:
throw new IllegalArgumentException( "The class kind=" + kind + " cannot be annotated" );
}
try {
XMLResource res = null;
String xquery = "/annotation/metadata[@subject=\"" + subject + "\"][@name=\"" +
primary + "\"]";
if (select == null) {
if (kind != CLASS_FILENAME) {
xquery += "[empty(@select)]";
}
} else {
xquery += "[@select=\"" + select + "\"]";
}
xquery += "/attribute[@name=\"" + key + "\"]";
if ((res = findAnnotation(xquery)) != null) {
String id = res.getDocumentId();
// get the document
XMLResource document = (XMLResource)m_meta.getResource(id);
m_meta.removeResource(document);
return true;
}
return false;
} catch (XMLDBException e) {
throw new SQLException(e.getMessage());
}
}
/**
* Deletes a specific key in an annotated transformation.
*
* @param fqdi is the FQDI of the transformation
* @param key is the key to search for
* @return true, if the database was modified, false otherwise.
* @see org.griphyn.vdl.classes.Transformation
*/
public boolean deleteAnnotationTransformation( String fqdi, String key )
throws SQLException, IllegalArgumentException
{
return deleteAnnotation(fqdi, null, CLASS_TRANSFORMATION, key);
}
/**
* Deletes a specific key in an annotated derivation.
*
* @param fqdi is the FQDI of the derivation
* @param key is the key to search for
* @return true, if the database was modified, false otherwise.
* @see org.griphyn.vdl.classes.Derivation
*/
public boolean deleteAnnotationDerivation( String fqdi, String key )
throws SQLException, IllegalArgumentException
{
return deleteAnnotation(fqdi, null, CLASS_DERIVATION, key);
}
/**
* Deletes a specific key in an annotated formal argument.
*
* @param fqdi is the FQDI of the transformation
* @param farg is the name of the formal argument
* @param key is the key to search for
* @return true, if the database was modified, false otherwise.
* @see org.griphyn.vdl.classes.Declare
*/
public boolean deleteAnnotationDeclare( String fqdi, String farg,
String key )
throws SQLException, IllegalArgumentException
{
return deleteAnnotation(fqdi, farg, CLASS_DECLARE, key);
}
/**
* Deletes a specific key for a call statement.
*
* @param fqdi is the FQDI of the transformation
* @param index is the number of the call to annotate.
* @param key is the key to search for
* @return true, if the database was modified, false otherwise.
* @see org.griphyn.vdl.classes.Call
*/
public boolean deleteAnnotationCall( String fqdi, int index,
String key )
throws SQLException, IllegalArgumentException
{
return deleteAnnotation(fqdi, new Integer(index), CLASS_CALL, key);
}
/**
* Deletes a specific key in an annotated filename.
*
* @param filename is the name of the file that was annotated.
* @param key is the key to search for
* @return true, if the database was modified, false otherwise.
* @see org.griphyn.vdl.classes.LFN
*/
public boolean deleteAnnotationFilename( String filename, String key )
throws SQLException, IllegalArgumentException
{
return deleteAnnotation(filename, null, CLASS_FILENAME, key);
}
/**
* Annotates a transformation with a tuple.
*
* @param fqdi is the FQDI to annotate
* @param annotation is the value to place
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see org.griphyn.vdl.classes.Transformation
*/
public long saveAnnotationTransformation( String fqdi,
Tuple annotation,
boolean overwrite )
throws SQLException, IllegalArgumentException
{
/*
try {
String key = annotation.getKey();
String type = annotation.getTypeString();
Object value = annotation.getValue();
Logging.instance().log( "nxd", 2, "INSERT INTO anno_tr" );
String id = null;
XMLResource res = null;
String xupdate =
"<xu:modifications version=\"1.0\" " +
"xmlns:xu=\"http://www.xmldb.org/xupdate\">";
String xquery = "//annotation/metadata[@subject='tr'][@name='" +
fqdi + "']";
if ((res = findAnnotation(xquery)) != null) {
//annotation for tr exists
id = res.getDocumentId();
String xquery_attr = xquery + "/attribute[@name='" + key + "']/text()";
if (findAnnotation(xquery_attr)!=null) {
//attribute already exists
Logging.instance().log( "nxd", 2, "Attribute already exists." );
if (!overwrite)
return -1;
xupdate += "<xu:update select=\"" + xquery + "\">" +
value + "</xu:update>";
//xupdate += "<xu:update select=\"" + xquery_attr + "/@type\">" +
//type + "</xu:update>";
} else {
//attribute does not exist
Logging.instance().log( "nxd", 2, "Attribute does not exist." );
xupdate += "<xu:append select=\"" + xquery + "\">" +
"<xu:element name=\"attribute\">" +
"<xu:attribute name=\"name\">" + key + "</xu:attribute>" +
"<xu:attribute name=\"type\">" + type + "</xu:attribute>" +
value + "</xu:element>" +
"</xu:append>";
}
xupdate += "</xu:modifications>";
System.out.println(xupdate);
long l = m_xupdQrySvc.update(xupdate);
} else {
//create the annotation
String anno = "<annotation><metadata subject=\"tr\" name=\"" + fqdi + "\">" +
"<attribute name=\"" + key + "\" type=\"" + type + "\">" + value + "</attribute>" +
"</metadata></annotation>";
// create new XMLResource; an id will be assigned to the new resource
XMLResource document = (XMLResource)m_meta.createResource(null, "XMLResource");
document.setContent(anno);
m_meta.storeResource(document);
}
return 0;
} catch (XMLDBException e) {
throw new SQLException(e.getMessage());
}
*/
return saveAnnotation(fqdi, null, CLASS_TRANSFORMATION, annotation, overwrite);
}
/**
* Annotates a derivation with a tuple.
*
* @param fqdi is the FQDI to annotate
* @param annotation is the value to place
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see org.griphyn.vdl.classes.Derivation
*/
public long saveAnnotationDerivation( String fqdi,
Tuple annotation,
boolean overwrite )
throws SQLException, IllegalArgumentException
{
return saveAnnotation(fqdi, null, CLASS_DERIVATION, annotation, overwrite);
}
/**
* Annotates a transformation argument with a tuple.
*
* @param fqdi is the FQDI to annotate
* @param formalname is the name of the formal argument to annotoate.
* @param annotation is the value to place
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see org.griphyn.vdl.classes.Declare
*/
public long saveAnnotationDeclare( String fqdi,
String formalname,
Tuple annotation,
boolean overwrite )
throws SQLException, IllegalArgumentException
{
return saveAnnotation(fqdi, formalname, CLASS_DECLARE, annotation, overwrite);
}
/**
* Annotates a transformation call with a tuple.
*
* @param fqdi is the FQDI to annotate
* @param index is the number of the call to annotate.
* @param annotation is the value to place
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see org.griphyn.vdl.classes.Call
*/
public long saveAnnotationCall( String fqdi,
int index,
Tuple annotation,
boolean overwrite )
throws SQLException, IllegalArgumentException
{
return saveAnnotation(fqdi, new Integer(index), CLASS_CALL, annotation, overwrite);
}
/**
* Annotates a logical filename with a tuple.
*
* @param filename is the FQDI to annotate
* @param annotation is the value to place
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see org.griphyn.vdl.classes.LFN
*/
public long saveAnnotationFilename( String filename,
Tuple annotation,
boolean overwrite )
throws SQLException, IllegalArgumentException
{
return saveAnnotation(filename, null, CLASS_FILENAME, annotation, overwrite);
}
/**
* Annotates any of the annotatable classes with the specified tuple.
* This is an interface method to the various class-specific methods.
*
* @param primary is the primary object specifier for the class.
* According to the type, this is either the FQDI, or the filename.
* @param secondary is a helper argument for annotations to calls
* and formal arguments, and should be null for all other classes.
* For calls, the argument must be packed into {@link java.lang.Integer}.
* @param kind defines the kind/class of object to annotate.
* @param annotation is the value to place into the class.
* @param overwrite is a predicate on replace or maintain.
* @return the insertion id, or -1, if the database was untouched
* @see #saveAnnotationTransformation( String, Tuple, boolean )
* @see #saveAnnotationDerivation( String, Tuple, boolean )
* @see #saveAnnotationCall( String, int, Tuple, boolean )
* @see #saveAnnotationDeclare( String, String, Tuple, boolean )
* @see #saveAnnotationFilename( String, Tuple, boolean )
*/
public long saveAnnotation( String primary, Object secondary,
int kind, Tuple annotation, boolean overwrite )
throws SQLException, IllegalArgumentException
{
long result = -1;
String subject = "";
String select = null;
String q_sec = null;
String defn = "transformation";
switch ( kind ) {
case CLASS_TRANSFORMATION:
subject = "tr";
break;
case CLASS_DERIVATION:
subject = "dv";
defn = "derivation";
break;
case CLASS_CALL:
// may throw ClassCastException
subject = "tr";
select = "call[" + ((Integer) secondary).intValue() + "]";
q_sec = select;
break;
case CLASS_DECLARE:
subject = "tr";
// may throw ClassCastException
q_sec = "declare[@name='" + (String)secondary + "']";
select = (String)secondary;
break;
case CLASS_FILENAME:
subject = "lfn";
break;
default:
throw new IllegalArgumentException( "The class kind=" + kind + " cannot be annotated" );
}
try {
if (kind != CLASS_FILENAME) {
String[] names = Separator.split(primary);
String q_ns, q_name, q_ver;
if (names[0] == null)
q_ns = "[empty(@namespace)]";
else
q_ns = "[@namespace='" + names[0] + "']";
if (names[1] == null)
q_name = "[empty(@name)]";
else
q_name = "[@name='" + names[1] + "']";
if (names[2] == null)
q_ver = "[empty(@version)]";
else
q_ver = "[@version='" + names[2] + "']";
//check if tr/dv is valid
String xquery = "//" + defn + q_ns + q_name + q_ver;
if (q_sec != null )
xquery += "/" + q_sec;
Logging.instance().log("nxd", 0, "query: " + xquery);
ResourceSet rs = m_vdcQrySvc.query(xquery);
ResourceIterator i = rs.getIterator();
if (!i.hasMoreResources()) {
Logging.instance().log("app", 0, "definition not found!");
return -1;
}
}
String key = annotation.getKey();
String type = annotation.getTypeString();
Object value = annotation.getValue();
String id = null;
XMLResource res = null;
String xquery = "/annotation/metadata[@subject=\"" + subject + "\"][@name=\"" +
primary + "\"]";
if (select == null) {
if (kind != CLASS_FILENAME) {
xquery += "[empty(@select)]";
}
} else {
xquery += "[@select=\"" + select + "\"]";
}
xquery += "/attribute[@name=\"" + key + "\"]";
if ((res = findAnnotation(xquery)) != null) {
if (!overwrite) {
System.err.println("key " + key + " already defined!");
return -1;
}
id = res.getDocumentId();
}
//create the annotation
String anno = "<annotation xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"><metadata subject=\"" + subject + "\" name=\"" + primary + "\"";
if (select !=null)
anno += " select=\"" + select + "\"";
anno += ">" + "<attribute name=\"" + key + "\" xsi:type=\"xs:" + type + "\">" + value + "</attribute>" +
"</metadata></annotation>";
// create new XMLResource; an id will be assigned to the new resource
XMLResource document = (XMLResource)m_meta.createResource(id, "XMLResource");
document.setContent(anno);
m_meta.storeResource(document);
return 0;
} catch (XMLDBException e) {
throw new SQLException(e.getMessage());
}
}
/**
* Obtains the value to a specific key in an annotated transformation.
*
* @param fqdi is the FQDI of the transformation
* @param key is the key to search for
* @return the annotated value, or null if not found.
* @see org.griphyn.vdl.classes.Transformation
*/
public Tuple loadAnnotationTransformation( String fqdi, String key )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, null, CLASS_TRANSFORMATION, key);
}
/**
* Obtains the value to a specific key in an annotated derivation.
*
* @param fqdi is the FQDI of the derivation
* @param key is the key to search for
* @return the annotated value, or null if not found.
* @see org.griphyn.vdl.classes.Derivation
*/
public Tuple loadAnnotationDerivation( String fqdi, String key )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, null, CLASS_DERIVATION, key);
}
/**
* Obtains the value to a specific key in an annotated formal argument.
*
* @param fqdi is the FQDI of the transformation
* @param farg is the name of the formal argument
* @param key is the key to search for
* @return the annotated value, or null if not found
* @see org.griphyn.vdl.classes.Declare
*/
public Tuple loadAnnotationDeclare( String fqdi, String farg,
String key )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, farg, CLASS_DECLARE, key);
}
/**
* Obtains the value to a specific key for a call statement.
*
* @param fqdi is the FQDI of the transformation
* @param index is the number of the call to annotate.
* @param key is the key to search for
* @return the annotated value, or null if not found
* @see org.griphyn.vdl.classes.Call
*/
public Tuple loadAnnotationCall( String fqdi, int index,
String key )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, new Integer(index), CLASS_CALL, key);
}
/**
* Obtains the value to a specific key in an annotated filename.
*
* @param filename is the name of the file that was annotated.
* @param key is the key to search for
* @return the annotated value, or null if not found.
* @see org.griphyn.vdl.classes.LFN
*/
public Tuple loadAnnotationFilename( String filename, String key )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(filename, null, CLASS_FILENAME, key);
}
/**
* Retrieves a specific annotation from an annotatable classes with
* the specified tuple. This is an interface method to the various
* class-specific methods.
*
* @param primary is the primary object specifier for the class.
* According to the type, this is either the FQDI, or the filename.
* @param secondary is a helper argument for annotations to calls
* and formal arguments, and should be null for all other classes.
* For calls, the argument must be packed into {@link java.lang.Integer}.
* @param kind defines the kind/class of object to annotate.
* @param key is the key to look for.
* @return null if not found, otherwise the annotation tuple.
* @see #loadAnnotationTransformation( String, String )
* @see #loadAnnotationDerivation( String, String )
* @see #loadAnnotationCall( String, int, String )
* @see #loadAnnotationDeclare( String, String, String )
* @see #loadAnnotationFilename( String, String )
*/
public Tuple loadAnnotation( String primary, Object secondary,
int kind, String key )
throws SQLException, IllegalArgumentException
{
Tuple result = null;
String subject = "";
String select = null;
switch ( kind ) {
case CLASS_TRANSFORMATION:
subject = "tr";
break;
case CLASS_DERIVATION:
subject = "dv";
break;
case CLASS_CALL:
// may throw ClassCastException
subject = "tr";
select = "call[" + ((Integer) secondary).intValue() + "]";
break;
case CLASS_DECLARE:
subject = "tr";
// may throw ClassCastException
//select = "declare[@name='" + (String)secondary + "']";
select = (String)secondary;
break;
case CLASS_FILENAME:
subject = "lfn";
break;
default:
throw new IllegalArgumentException( "The class kind=" + kind + " cannot be annotated" );
}
try {
String id = null;
String xquery = "/annotation/metadata[@subject=\"" + subject + "\"][@name=\"" +
primary + "\"]";
if (select == null) {
if (kind != CLASS_FILENAME) {
xquery += "[empty(@select)]";
}
} else {
xquery += "[@select=\"" + select + "\"]";
}
xquery += "/attribute[@name=\"" + key + "\"]";
XMLResource res = null;
if ((res = findAnnotation(xquery)) != null) {
result = loadAnnotationResource(res);
}
return result;
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
}
/**
* get the annotation from a XML resource
*/
protected Tuple loadAnnotationResource(XMLResource res)
throws SQLException
{
Tuple result = null;
if (res == null)
return result;
Element elem;
try {
elem = (Element)res.getContentAsDOM();
} catch (Exception e) {
throw new SQLException( e.getMessage());
}
if (elem != null) {
String key = elem.getAttribute("name");
String type = elem.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance","type");
String value = elem.getFirstChild().getNodeValue();
if (key == null || type == null || value == null)
return result;
if (type.equals("xs:string")) {
result = new TupleString(key, null);
result.setValue(value);
return result;
}
if (type.equals("xs:float")){
result = new TupleFloat(key, 0);
result.setValue(value);
return result;
}
if (type.equals("xs:int")){
result = new TupleInteger(key, 0);
result.setValue(value);
return result;
}
if (type.equals("xs:boolean")) {
result = new TupleBoolean(key, false);
result.setValue(value);
return result;
}
if (type.equals("xs:date")) {
result = new TupleDate(key, null);
result.setValue(value);
return result;
}
}
return result;
}
/**
* Lists all annotations for a transformation.
*
* @param fqdi is the FQDI of the transformation
* @return a list of tuples, which may be empty.
* @see org.griphyn.vdl.classes.Transformation
*/
public java.util.List loadAnnotationTransformation( String fqdi )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, null, CLASS_TRANSFORMATION);
}
/**
* Lists all annotations for a derivation.
*
* @param fqdi is the FQDI of the derivation
* @return a list of tuples, which may be empty.
* @see org.griphyn.vdl.classes.Derivation
*/
public java.util.List loadAnnotationDerivation( String fqdi )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, null, CLASS_DERIVATION);
}
/**
* Lists all annotations for a formal argument.
*
* @param fqdi is the FQDI of the transformation
* @param farg is the name of the formal argument
* @return a list of tuples, which may be empty.
* @see org.griphyn.vdl.classes.Declare
*/
public java.util.List loadAnnotationDeclare( String fqdi, String farg )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, farg, CLASS_DECLARE);
}
/**
* Lists all annotations for a call statement.
*
* @param fqdi is the FQDI of the transformation
* @param index is the number of the call to annotate.
* @return a list of tuples, which may be empty.
* @see org.griphyn.vdl.classes.Call
*/
public java.util.List loadAnnotationCall( String fqdi, int index )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(fqdi, new Integer(index), CLASS_CALL);
}
/**
* Lists all annotations for a logical filename.
*
* @param filename is the logical filename.
* @return a list of tuples, which may be empty.
* @see org.griphyn.vdl.classes.LFN
*/
public java.util.List loadAnnotationFilename( String filename )
throws SQLException, IllegalArgumentException
{
return loadAnnotation(filename, null, CLASS_FILENAME);
}
/**
* Retrieves all annotations from an annotatable classes with
* the specified tuple. This is an interface method to the various
* class-specific methods.
*
* @param primary is the primary object specifier for the class.
* According to the type, this is either the FQDI, or the filename.
* @param secondary is a helper argument for annotations to calls
* and formal arguments, and should be null for all other classes.
* For calls, the argument must be packed into {@link java.lang.Integer}.
* @param kind defines the kind/class of object to annotate.
*
* @return null if not found, otherwise the annotation tuple.
* @see #loadAnnotationTransformation( String )
* @see #loadAnnotationDerivation( String )
* @see #loadAnnotationCall( String, int )
* @see #loadAnnotationDeclare( String, String )
* @see #loadAnnotationFilename( String )
*/
public java.util.List loadAnnotation( String primary,
Object secondary,
int kind )
throws SQLException, IllegalArgumentException
{
java.util.List result = new java.util.ArrayList();
String subject = "";
String select = null;
switch ( kind ) {
case CLASS_TRANSFORMATION:
subject = "tr";
break;
case CLASS_DERIVATION:
subject = "dv";
break;
case CLASS_CALL:
// may throw ClassCastException
subject = "tr";
select = "call[" + ((Integer) secondary).intValue() + "]";
break;
case CLASS_DECLARE:
subject = "tr";
// may throw ClassCastException
//select = "declare[@name='" + (String)secondary + "']";
select = (String)secondary;
break;
case CLASS_FILENAME:
subject = "lfn";
break;
default:
throw new IllegalArgumentException( "The class kind=" + kind + " cannot be annotated" );
}
try {
String id = null;
String xquery = "/annotation/metadata[@subject=\"" + subject + "\"][@name=\"" +
primary + "\"]";
if (select == null) {
if (kind != CLASS_FILENAME) {
xquery += "[empty(@select)]";
}
} else {
xquery += "[@select=\"" + select + "\"]";
}
xquery += "/attribute";
Logging.instance().log("nxd", 1, "query: " + xquery );
ResourceSet rs = m_metaQrySvc.query(xquery);
ResourceIterator i = rs.getIterator();
while (i.hasMoreResources()) {
XMLResource res = (XMLResource)i.nextResource();
Tuple tuple = loadAnnotationResource(res);
if (tuple != null) {
result.add(tuple);
}
}
return result;
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
}
/**
* Search for LFNs or Definitions that has certain annotations
*
* @param kind defines the kind/class of object annotated.
* @param arg is used only for TR ARG and TR CALL. For the former
* it is the name of the argument (String), for the latter the position of
* the call (Integer).
* @param tree stores the query tree to query the annotation
* @return a list of LFNs if search for filenames, otherwise a list of
* definitions.
* @exception SQLException if something goes wrong with the database.
* @see org.griphyn.vdl.annotation.QueryTree
*/
public java.util.List searchAnnotation( int kind,
Object arg,
QueryTree tree)
throws SQLException
{
java.util.List result = new java.util.ArrayList();
if ( tree == null)
return result;
String subject = "";
String defn = "transformation";
String select = null;
switch ( kind ) {
case CLASS_TRANSFORMATION:
subject = "tr";
break;
case CLASS_DERIVATION:
subject = "dv";
defn = "derivation";
break;
case CLASS_CALL:
// may throw ClassCastException
subject = "tr";
select = "call[" + ((Integer) arg).intValue() + "]";
break;
case CLASS_DECLARE:
subject = "tr";
// may throw ClassCastException
//select = "declare[@name='" + (String)arg + "']";
select = (String)arg;
break;
case CLASS_FILENAME:
subject = "lfn";
break;
default:
throw new IllegalArgumentException( "The class kind=" + kind + " cannot be annotated" );
}
try {
String id = null;
String cond = "[@subject=\"" + subject + "\"]";
if (select == null) {
if (kind != CLASS_FILENAME) {
cond += "[empty(@select)]";
}
} else {
cond += "[@select=\"" + select + "\"]";
}
String xquery = "for $mn in distinct-values(//annotation/metadata" + cond + "/@name) " +
"let $m := //annotation/metadata[@name=$mn]" + cond;
String where = " where ";
where += tree.toXQuery("$m/attribute");
if (kind == CLASS_FILENAME) {
xquery += ", $r := $m";
xquery += where;
xquery += " return $mn";
return searchElements(xquery);
} else {
xquery += ", $n := substring-before($mn, '::'), $na := substring-after($mn, '::'), $iv := if ($na) then $na else $mn, $v := substring-after($iv, ':'), $ib := substring-before($iv, ':'), $i := if ($ib) then $ib else $iv,";
xquery += " $t := if ($n) then if ($v) then //" + defn + "[@namespace=$n][@name=$i][@version=$v] else //" + defn + "[@namespace=$n][@name=$i][empty(@version)] else if ($v) then //" + defn + "[empty(@namespace)][@name=$i][@version=$v] else //" + defn + "[empty(@namespace)][@name=$i][empty(@version)]";
xquery += where;
if (kind == CLASS_DECLARE)
xquery += " return $t[" + "declare[@name='" + select + "']" + "]";
else
xquery += " return $t";
return searchDefinition(xquery);
}
} catch (Exception e) {
throw new SQLException(e.getMessage());
}
}
/**
* pass-thru to driver.
* @return true, if it is feasible to cache results from the driver
* false, if requerying the driver is sufficiently fast (e.g. driver
* is in main memory, or driver does caching itself).
*/
public boolean cachingMakesSense()
{
return true;
}
public void close()
throws SQLException
{
try {
//m_vdc.close();
//m_db.close();
} catch (Exception e){
throw new SQLException(e.getMessage());
}
}
}