572 lines
16 KiB
Java
Executable File
572 lines
16 KiB
Java
Executable File
package org.gcube.informationsystem.collector.impl.persistence;
|
|
|
|
import org.gcube.common.core.utils.logging.GCUBELog;
|
|
import org.gcube.informationsystem.collector.impl.persistence.AggregatorPersistentResource;
|
|
|
|
import org.xmldb.api.modules.XMLResource;
|
|
import org.xmldb.api.base.XMLDBException;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.IOException;
|
|
import java.io.StringReader;
|
|
import java.io.StringWriter;
|
|
import java.util.Calendar;
|
|
import java.util.GregorianCalendar;
|
|
import java.util.TimeZone;
|
|
|
|
import java.lang.Exception;
|
|
|
|
import org.w3c.dom.*;
|
|
|
|
import org.xml.sax.InputSource;
|
|
|
|
import javax.xml.parsers.*;
|
|
import javax.xml.transform.OutputKeys;
|
|
import javax.xml.transform.Transformer;
|
|
import javax.xml.transform.TransformerConfigurationException;
|
|
import javax.xml.transform.TransformerException;
|
|
import javax.xml.transform.TransformerFactory;
|
|
import javax.xml.transform.dom.DOMSource;
|
|
import javax.xml.transform.stream.StreamResult;
|
|
import javax.xml.xpath.*;
|
|
|
|
/**
|
|
* AggregatorPersistentResource represents a resource in the XML Storage
|
|
* repository
|
|
*
|
|
* @author Manuele Simi (ISTI-CNR)
|
|
*
|
|
*/
|
|
|
|
public class AggregatorPersistentResource extends PersistentResource {
|
|
|
|
private String resourceID = null;
|
|
|
|
private String resource_string = null;
|
|
|
|
// the resource type states if the resource contains a profile or generic
|
|
// WS-ResourceProperties
|
|
private RESOURCETYPE type = null;
|
|
|
|
// the profile type states which kind of profiles the resource contains
|
|
// (RunningInstance, Service, etc.)
|
|
private String profile_type = null;
|
|
|
|
// the various parts of the resource
|
|
private String data = null, entryKey, groupKey, source, sourceKey = "", completeSourceKey = "";
|
|
|
|
private Calendar terminationTime = null, lastUpdateTime = null;
|
|
|
|
private static GCUBELog logger = new GCUBELog(AggregatorPersistentResource.class);
|
|
|
|
// the source XMLResource (if any)
|
|
private XMLResource originalSource = null;
|
|
|
|
// the XML DOM loaded from the resource string
|
|
private Document internalDOM = null;
|
|
|
|
// xpath factory to evaluate Xpath expressions
|
|
private XPath path = XPathFactory.newInstance().newXPath();
|
|
|
|
/**
|
|
* Builds a new empty DISPersinstentresource
|
|
* @param key
|
|
* @param source
|
|
* @throws Exception if it was impossible to extract the ID from the resource
|
|
*
|
|
*/
|
|
public AggregatorPersistentResource(String source, String key, String data, RESOURCETYPE type) throws Exception {
|
|
|
|
// defatult termination time is now
|
|
Calendar cal = new GregorianCalendar();
|
|
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
this.setTerminationTime(cal);
|
|
this.source = source;
|
|
this.sourceKey = key;
|
|
this.data = data;
|
|
this.type = type;
|
|
lastUpdateTime = new GregorianCalendar();
|
|
lastUpdateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
this.buildID();
|
|
logger.debug("Resource ID: " + this.resourceID);
|
|
}
|
|
|
|
|
|
/**
|
|
* Builds a DISPersinstentresource starting from an eXist resource
|
|
*
|
|
* @param resource
|
|
* the input eXist resource
|
|
* @throws Exception
|
|
* if the resource is invalid
|
|
*/
|
|
public AggregatorPersistentResource(XMLResource resource) throws Exception {
|
|
if (resource == null)
|
|
throw new Exception("invalid resource");
|
|
try {
|
|
this.resourceID = resource.getId();
|
|
this.originalSource = resource;
|
|
this.resource_string = resource.getContent().toString();
|
|
this.parseResource();
|
|
logger.debug("Resource ID: " + this.resourceID);
|
|
} catch (XMLDBException dbe) {
|
|
throw new Exception("invalid resource");
|
|
}
|
|
}
|
|
|
|
private void buildID() throws Exception {
|
|
if ((this.type != null) && (type == RESOURCETYPE.Profile)) { //it's a gCube profile
|
|
try {
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
StringReader reader = new StringReader(this.toXML());
|
|
InputSource source = new InputSource(reader);
|
|
Document internalDOM = builder.parse(source);
|
|
// gets the type
|
|
XPath path = XPathFactory.newInstance().newXPath();
|
|
this.profile_type = path.evaluate("/Document/Data/child::*[local-name()='Profile']/Resource/Type", internalDOM);
|
|
// uses the GCUBEResource ID as local resource ID
|
|
this.resourceID = path.evaluate("/Document/Data/child::*[local-name()='Profile']/Resource/ID", internalDOM);
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Unable to extract the ID from the resource " + e.getMessage());
|
|
throw e;
|
|
|
|
}
|
|
} else { //it's a RP doc
|
|
//we need to replace the schema and colons in order to make it an ID accepted by eXist
|
|
this.resourceID = this.source.replace("http://", "").replace(":", "").replace("/", "-") + "-" + this.sourceKey.replace("http://", "").replace(":", "").replace("/", "-");
|
|
}
|
|
}
|
|
/**
|
|
* Sets the content of the resource
|
|
*
|
|
* @param data the new content
|
|
*/
|
|
public void setData(String data) {
|
|
this.data = data;
|
|
}
|
|
|
|
/**
|
|
* Sets the content of the resource using the content of a file
|
|
*
|
|
* @param f the file to use as content source
|
|
*
|
|
* @throws IOException if the access to the given file fails
|
|
*
|
|
*/
|
|
public void setData(File f) throws IOException {
|
|
try {
|
|
FileInputStream fin = new FileInputStream(f);
|
|
this.data = fin.toString();
|
|
} catch (IOException ioe) {
|
|
logger.error("Unable to set content from file " + f.getAbsolutePath());
|
|
throw ioe;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accesses the resource content
|
|
*
|
|
* @return the content
|
|
* @throws MalformedResourceException
|
|
*/
|
|
public String getData() throws MalformedResourceException {
|
|
if (this.data == null)
|
|
this.data = this.extractData();
|
|
return this.data;
|
|
}
|
|
|
|
/**
|
|
* @return
|
|
* @throws MalformedResourceException
|
|
*/
|
|
private String extractData() throws MalformedResourceException {
|
|
|
|
if (this.internalDOM != null) {
|
|
try {
|
|
if (this.getType() == RESOURCETYPE.Profile)
|
|
return nodeToString(internalDOM.getElementsByTagName("Resource").item(0)); //Data/Profile/Resource
|
|
else
|
|
return nodeToString(internalDOM.getElementsByTagName("Data").item(0)); //Data/*
|
|
} catch (Exception e) {
|
|
logger.error("",e);
|
|
throw new MalformedResourceException("unable to retrieve data for resource " + this.getID());
|
|
}
|
|
} else
|
|
throw new MalformedResourceException("unable to retrieve data for resource " + this.getID());
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @return the original XMLResource from which the resource has been
|
|
* generated (if any)
|
|
*/
|
|
public XMLResource getOriginalXMLResource() {
|
|
return this.originalSource;
|
|
}
|
|
|
|
/**
|
|
* Accesses the resource ID
|
|
*
|
|
* @return the ID
|
|
*/
|
|
public String getID() {
|
|
return this.resourceID;
|
|
}
|
|
|
|
/**
|
|
* Accesses the source GroupKey
|
|
*
|
|
* @return the ID
|
|
*/
|
|
public String getGroupKey() {
|
|
return this.groupKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the source GroupKey
|
|
*
|
|
* @param groupKey
|
|
* the new group key
|
|
*
|
|
*/
|
|
public void setGroupKey(String groupKey) {
|
|
this.groupKey = groupKey;
|
|
}
|
|
|
|
/**
|
|
* Accesses the source EntryKey
|
|
*
|
|
* @return the ID
|
|
*/
|
|
public String getEntryKey() {
|
|
return this.entryKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the source EntryKey
|
|
*
|
|
* @param entryKey
|
|
* the new entry key
|
|
*/
|
|
public void setEntryKey(String entryKey) {
|
|
this.entryKey = entryKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the source address of the RI that publishes resource as reported in
|
|
* the servicegroup entry
|
|
*
|
|
* @param source
|
|
* the new source address
|
|
*/
|
|
public void setSource(String source) {
|
|
this.source = source;
|
|
}
|
|
|
|
/**
|
|
* Accesses the source address of the service that published the data
|
|
*
|
|
* @return the source
|
|
*/
|
|
public String getSource() {
|
|
return this.source;
|
|
}
|
|
|
|
/**
|
|
* Sets the key of the WS-Resource that published the data
|
|
*
|
|
* @param key
|
|
* the new source key
|
|
*/
|
|
public void setSourceKey(String key) {
|
|
this.sourceKey = key;
|
|
}
|
|
|
|
/**
|
|
* Gets the key of the WS-Resource that published the data
|
|
*
|
|
* @return the key
|
|
*/
|
|
public String getSourceKey() {
|
|
return this.sourceKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the complete source key
|
|
*
|
|
* @param completeKey the new complete key
|
|
*/
|
|
public void setCompleteSourceKey(String completeKey) {
|
|
this.completeSourceKey = completeKey;
|
|
}
|
|
|
|
/**
|
|
* Gets the complete source key
|
|
*
|
|
* @return the complete source key
|
|
*/
|
|
public String getCompleteSourceKey() {
|
|
return this.completeSourceKey;
|
|
}
|
|
|
|
/**
|
|
* Sets the resource type. The actual implementation supports "Profile" and
|
|
* "Properties" as type
|
|
*
|
|
* @param type
|
|
* "Profile" or "Properties"
|
|
*/
|
|
|
|
public void setType(RESOURCETYPE type) {
|
|
this.type = type;
|
|
}
|
|
|
|
/**
|
|
* Accesses the resource type. The actual implementation supports "Profile"
|
|
* and "Properties" as type
|
|
*
|
|
* @return the type
|
|
*/
|
|
public RESOURCETYPE getType() {
|
|
if (this.type == null)
|
|
this.type = this.extractType();
|
|
return this.type;
|
|
}
|
|
|
|
/**
|
|
* @return
|
|
*/
|
|
private RESOURCETYPE extractType() {
|
|
if (internalDOM.getElementsByTagName("Resource").getLength() > 0) {
|
|
logger.trace("The resource is a profile");
|
|
return RESOURCETYPE.Profile;
|
|
}
|
|
else {
|
|
logger.trace("The resource is a RP");
|
|
return RESOURCETYPE.Properties;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Updates the resource body
|
|
*
|
|
* @param data
|
|
* the new body
|
|
*
|
|
*/
|
|
public void updateData(String data) {
|
|
this.data = data;
|
|
}
|
|
|
|
/**
|
|
* Builds a XML representation of the resource
|
|
*
|
|
* @return a String in the form of <Document>... <Data> resource
|
|
* content </Data> </Document>
|
|
*/
|
|
private String toXML() {
|
|
|
|
StringBuilder resource = new StringBuilder("<Document>\n");
|
|
resource.append("<ID>" + this.getID() + "</ID>\n");
|
|
resource.append("<Source>" + this.getSource() + "</Source>\n");
|
|
resource.append("<SourceKey>" + this.getSourceKey() + "</SourceKey>\n");
|
|
resource.append("<CompleteSourceKey>" + this.getCompleteSourceKey() + "</CompleteSourceKey>\n");
|
|
resource.append("<EntryKey>" + this.getEntryKey() + "</EntryKey>\n");
|
|
resource.append("<GroupKey>" + this.getGroupKey() + "</GroupKey>\n");
|
|
resource.append("<TerminationTime>"+ this.getTerminationTime().getTimeInMillis()+ "</TerminationTime>\n");
|
|
resource.append("<TerminationTimeHuman>" + this.getTerminationTime().getTime().toString()+ "</TerminationTimeHuman>\n");
|
|
resource.append("<LastUpdateMs>" + this.lastUpdateTime.getTimeInMillis()+ "</LastUpdateMs>\n");
|
|
resource.append("<LastUpdateHuman>" + this.lastUpdateTime.getTime().toString() + "</LastUpdateHuman>\n");
|
|
try {
|
|
resource.append(this.getData() + "\n");
|
|
} catch (MalformedResourceException e) {
|
|
logger.warn("Unable to read the resource data",e);
|
|
}
|
|
resource.append( "</Document>\n");
|
|
return resource.toString();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return a String representation of the resource
|
|
*/
|
|
public String toString() {
|
|
if (this.resource_string == null)
|
|
return this.toXML();
|
|
else
|
|
return this.resource_string;
|
|
}
|
|
|
|
/**
|
|
* Accesses the type of resource to which the profile is related to (if any)
|
|
*
|
|
* @return null if the resource does not contain a profile
|
|
*/
|
|
public String getProfileType() {
|
|
if (this.profile_type != null) {
|
|
// the profile type has been already extracted
|
|
return this.profile_type;
|
|
}
|
|
|
|
if ((this.type != null) && (type == RESOURCETYPE.Profile)) {
|
|
try {
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
StringReader reader = new StringReader(this.toXML());
|
|
InputSource source = new InputSource(reader);
|
|
Document internalDOM = builder.parse(source);
|
|
// gets the type
|
|
XPath path = XPathFactory.newInstance().newXPath();
|
|
this.profile_type = path.evaluate("/Document/Data/child::*[local-name()='Profile']/Resource/Type", internalDOM);
|
|
// uses the GCUBEResource ID as local resource ID
|
|
this.resourceID = path.evaluate("/Document/Data/child::*[local-name()='Profile']/Resource/ID", internalDOM);
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Unable to extract the profile type from the resource " + e.getMessage());
|
|
|
|
}
|
|
}
|
|
return this.profile_type;
|
|
}
|
|
|
|
/**
|
|
* @return the terminationTime of this resource
|
|
*/
|
|
public Calendar getTerminationTime() {
|
|
return terminationTime;
|
|
}
|
|
|
|
/**
|
|
* @param terminationTime
|
|
* the terminationTime to set
|
|
*/
|
|
public void setTerminationTime(Calendar terminationTime) {
|
|
this.terminationTime = (Calendar) terminationTime.clone();
|
|
this.terminationTime.setTimeZone(TimeZone.getTimeZone("GMT"));
|
|
}
|
|
|
|
/**
|
|
* Compares two resources
|
|
*
|
|
* @param o
|
|
* the resource to compare
|
|
* @return true if the resources have the same ID
|
|
*/
|
|
public boolean equals(Object o) {
|
|
// check the class this object is instance of
|
|
if (!(o instanceof AggregatorPersistentResource))
|
|
return false;
|
|
// compare the two objects
|
|
AggregatorPersistentResource key = (AggregatorPersistentResource) o;
|
|
if (key.getID() == this.getID())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return the lastUpdateTime in milliseconds
|
|
* @throws Exception if an error occurs when accessing the LastUpdateMs field
|
|
*/
|
|
public long getLastUpdateTimeinMills() throws MalformedResourceException {
|
|
|
|
if (lastUpdateTime != null)
|
|
return lastUpdateTime.getTimeInMillis();
|
|
|
|
if ((lastUpdateTime == null) && (this.internalDOM != null)) {
|
|
String value = "";
|
|
try {
|
|
value = path.evaluate("Document/LastUpdateMs", this.internalDOM);
|
|
} catch (XPathExpressionException xpee) {
|
|
logger.error("" + xpee.getMessage());
|
|
logger.error("" + xpee.getStackTrace());
|
|
throw new MalformedResourceException("XPath evaluation error");
|
|
}
|
|
try {
|
|
return Long.parseLong(value);
|
|
} catch (NumberFormatException nfe) {
|
|
logger.error("Invalid last update time format found in resource " + this.getID());
|
|
logger.error("Parsed string was " + value);
|
|
throw new MalformedResourceException("Unable to retrieve last update time for resource " + this.getID());
|
|
}
|
|
|
|
} else
|
|
throw new MalformedResourceException("unable to retrieve last update time for resource " + this.getID());
|
|
|
|
}
|
|
|
|
/**
|
|
* Loads the XML DOM from the resource string
|
|
*
|
|
*/
|
|
private void parseResource() throws MalformedResourceException {
|
|
logger.debug("Parsing resource: " + this.resource_string);
|
|
try {
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
StringReader reader = new StringReader(this.resource_string);
|
|
InputSource source = new InputSource(reader);
|
|
this.internalDOM = builder.parse(source);
|
|
} catch (Exception e) {
|
|
throw new MalformedResourceException(e);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see
|
|
* org.gcube.informationsystem.collector.impl.persistence.PersistentResource
|
|
* #getPublisher()
|
|
*/
|
|
@Override
|
|
public String getPublisher() {
|
|
return null;
|
|
}
|
|
|
|
|
|
/*
|
|
* (non-Javadoc)
|
|
*
|
|
* @see
|
|
* org.gcube.informationsystem.collector.impl.persistence.PersistentResource
|
|
* #setPublisher(java.lang.String)
|
|
*/
|
|
@Override
|
|
public void setPublisher(String publisher) {
|
|
|
|
}
|
|
|
|
/**
|
|
* Node to String helper
|
|
* @param node the source node
|
|
* @return the node content serialized as string
|
|
* @throws Exception if the serialization fails
|
|
*/
|
|
private String nodeToString(Node node) throws Exception{
|
|
try {
|
|
TransformerFactory transFactory = TransformerFactory.newInstance();
|
|
Transformer transformer = transFactory.newTransformer();
|
|
StringWriter buffer = new StringWriter();
|
|
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
|
transformer.transform(new DOMSource(node), new StreamResult(buffer));
|
|
return buffer.toString();
|
|
|
|
} catch (TransformerConfigurationException e) {
|
|
logger.error("Unable to deserialise content data", e);
|
|
throw new Exception("Unable to deserialise content data");
|
|
} catch (TransformerException e) {
|
|
logger.error("Unable to deserialise content data", e);
|
|
throw new Exception("Unable to deserialise content data");
|
|
}
|
|
|
|
}
|
|
}
|