is-collector/src/org/gcube/informationsystem/collector/impl/persistence/AggregatorPersistentResourc...

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");
}
}
}