/*
* Copyright (c) 2003-2010 The Regents of the University of California.
* All rights reserved.
*
* '$Author: tao $'
* '$Date: 2011-04-06 18:22:21 -0700 (Wed, 06 Apr 2011) $'
* '$Revision: 27446 $'
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the above
* copyright notice and the following two paragraphs appear in all copies
* of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
* THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
* PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
* CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
* ENHANCEMENTS, OR MODIFICATIONS.
*
*/
package org.ecoinformatics.seek.ecogrid;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.UUID;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xpath.XPathAPI;
import org.ecoinformatics.ecogrid.EcogridObjType;
import org.ecoinformatics.ecogrid.client.AuthenticationServiceClient;
import org.ecoinformatics.ecogrid.client.PutServiceClient;
import org.ecoinformatics.seek.datasource.eml.eml2.Eml200Parser;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.actor.TypedIOPort;
import ptolemy.data.ObjectToken;
import ptolemy.data.StringToken;
import ptolemy.data.expr.Parameter;
import ptolemy.data.expr.StringParameter;
import ptolemy.data.type.BaseType;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;
import util.StaticUtil;
/**
* This actor will write a data with metadata document into Ecogrid service.
*
* @author tao
*/
public class EcogridWriter extends TypedAtomicActor {
// /////////////////////////////////////////////////////////////////
// // ports and parameters ////
/*
* The input port, which is a multiport.
*/
// public TypedIOPort trigger;
/* Source file to be uploaded */
/**
* Input port. This should be a file path value of a local data file which
* will be upload to Ecogrid service
*/
public TypedIOPort sourceFileNamePort = null;
public TypedIOPort sourceDataPort;
/**
* Input port. This should a be string which is metadata describing the data
* file. This string will be uploaded to Ecogrid service as metadata.
*/
public TypedIOPort metadataPort = null;
/**
* Output port. This will be the metadata docid which generated by the actor
* for future reference
*/
public TypedIOPort metadataDocidPort = null;
/**
* Output port. This will be the data docid which generated by the actor for
* future reference
*/
public TypedIOPort dataDocidPort = null;
private String metadataDocid = null;
private String dataDocid = null;
private String metadataUserName = null;
private String metadataPasswd = null;
private String metadataDestination = null;
// private String dataUserName = null;
// private String dataPasswd = null;
// private String dataDestination = null;
private String authenURL = null;
private String localDataFileName = null;
private byte[] localData;
private String metadataContent = null;
protected final static Log log;
protected final static boolean isDebugging;
static {
log = LogFactory
.getLog("org.ecoinformatics.seek.ecogrid.EcoGridServicesController");
isDebugging = log.isDebugEnabled();
}
private String docIdSuffix = "doc";
private static final String DATAFILENAMEPORT = "dataFileNamePort";
private static final String DATAPORT = "dataPort";
private static final String METADATAPORT = "metadata";
private static final String METADATADOCIDPORT = "metadataDocid";
private static final String DATADOCIDPORT = "dataDocid";
private static final String METADATADESTINATION = "metadataDestination";
private static final String DISTRIBUTIONPATH = "physical/distribution/online/url";
private static final String SEPERATOR = ".";
private static final String ECOGRIDPROTOCOL = "ecogrid://knb/";
private static final String DEFAULTECOGRIDPUTSERVER = "http://ecogrid.ecoinformatics.org/knb/services/PutService";
private static final String DEFAULTECOGRIDAUTHENSERVER = "http://ecogrid.ecoinformatics.org/knb/services/AuthenticationService";
private static final String AUTHENURL = "authenticationURL";
private static final String USERNAME = "userName";
private static final String PASSWORD = "passWord";
/**
* Ecogrid service URL for receiving metadata and data
*/
public StringParameter metadataDesParam = null;
/**
* Ecogrid service URL for authenticating user
*/
public StringParameter authenURLParam = null;
/**
* User name for authenticatication. For example, it is a DN for knb ldap
* server. uid=smith,o=NCEAS,dc=eocinformatics,dc=org
*/
public StringParameter usernameParam = null;
/**
* Password for this user
*/
public StringParameter passwordParam = null;
// public FileParameter localDataFileNameParameter = null;
public EcogridWriter(CompositeEntity container, String name)
throws IllegalActionException, NameDuplicationException {
super(container, name);
// input ports
metadataPort = new TypedIOPort(this, METADATAPORT, true, false);
metadataPort.setMultiport(false);
metadataPort.setTypeEquals(BaseType.STRING);
sourceFileNamePort = new TypedIOPort(this, DATAFILENAMEPORT, true,
false);
sourceFileNamePort.setMultiport(false);
sourceFileNamePort.setTypeEquals(BaseType.STRING);
sourceDataPort = new TypedIOPort(this, DATAPORT, true, false);
sourceDataPort.setTypeEquals(BaseType.OBJECT);
// output ports
metadataDocidPort = new TypedIOPort(this, METADATADOCIDPORT, false,
true);
metadataDocidPort.setMultiport(false);
metadataDocidPort.setTypeEquals(BaseType.STRING);
dataDocidPort = new TypedIOPort(this, DATADOCIDPORT, false, true);
dataDocidPort.setMultiport(false);
dataDocidPort.setTypeEquals(BaseType.STRING);
// parameters
metadataDesParam = new StringParameter(this, METADATADESTINATION);
metadataDesParam.setExpression(DEFAULTECOGRIDPUTSERVER);
authenURLParam = new StringParameter(this, AUTHENURL);
authenURLParam.setExpression(DEFAULTECOGRIDAUTHENSERVER);
usernameParam = new StringParameter(this, USERNAME);
passwordParam = new StringParameter(this, PASSWORD);
// localDataFileNameParameter = new FileParameter(this,
// LOCATDATFILENAME);
_attachText("_iconDescription", "\n");
}
// /////////////////////////////////////////////////////////////////
// // public methods ////
/**
* If the specified attribute is fileOrURL and there is an open file
* being read, then close that file and open the new one; if the attribute
* is numberOfLinesToSkip and its value is negative, then throw an
* exception. In the case of fileOrURL , do nothing if the file name
* is the same as the previous value of this attribute.
*
* @param attribute
* The attribute that has changed.
* @exception IllegalActionException
* If the specified attribute is fileOrURL and the
* file cannot be opened, or the previously opened file
* cannot be closed; or if the attribute is
* numberOfLinesToSkip and its value is negative.
*/
/**
* Determine the attribute changed value
*
* @param attribute
* The attribute that changed.
* @exception IllegalActionException
* If the output type is not recognized.
*/
public void attributeChanged(Attribute attribute)
throws IllegalActionException {
if (attribute == metadataDesParam) {
metadataDestination = getValueForAttributeChange(metadataDesParam);
} else if (attribute == authenURLParam) {
authenURL = getValueForAttributeChange(authenURLParam);
;
} else if (attribute == usernameParam) {
metadataUserName = getValueForAttributeChange(usernameParam);
} else if (attribute == passwordParam) {
metadataPasswd = getValueForAttributeChange(passwordParam);
}
}
/*
* Get new value for attribute changes
*/
private String getValueForAttributeChange(Parameter attribute)
throws IllegalActionException{
String newValue = null;
if(attribute != null ) {
StringToken token = (StringToken)attribute.getToken();
if(token != null) {
newValue = token.stringValue();
}
}
if (isDebugging) {
log.debug("The value of attribute is " + newValue);
}
//System.out.println("======the value of attribute is "+newValue);
return newValue;
}
/**
* @return Description of the Returned Value
* @exception IllegalActionException
* Description of Exception
* @since
*/
public boolean prefire() throws IllegalActionException {
int revision = 1;
if(sourceDataPort.numberOfSources() > 0) {
ObjectToken object = (ObjectToken) sourceDataPort.get(0);
localData = (byte[]) object.getValue();
}
else if (sourceFileNamePort.numberOfSources() > 0) {
// get data file name
if (!sourceFileNamePort.hasToken(0))
return false;
StringToken sourcefnToken = (StringToken) sourceFileNamePort.get(0);
localDataFileName = sourcefnToken.stringValue();
// localDataFileNameParameter.setExpression(localDataFileName);
if (isDebugging) {
log.debug("The localDataFileName is " + localDataFileName);
}
}
/*
* else { localDataFileName =
* localDataFileNameParameter.asFile().getPath(); }
*/
dataDocid = generateDocId(docIdSuffix, revision);
// get metadata
if (metadataPort.getWidth() > 0) {
if (!metadataPort.hasToken(0))
return false;
StringToken metadataToken = (StringToken) metadataPort.get(0);
metadataContent = metadataToken.stringValue();
if (isDebugging) {
log.debug("The original metadata is " + metadataContent);
}
// replace the url part
String newURL = ECOGRIDPROTOCOL + dataDocid;
metadataDocid = generateDocId(docIdSuffix, revision);
try {
metadataContent = replaceDistributionURLAndPackageID(metadataContent,
newURL, metadataDocid);
} catch (Exception e) {
throw new IllegalActionException(e.getMessage());
}
}
return super.prefire();
}
/**
* Output the data lines into an array.
*
* @exception IllegalActionException
* If there's no director.
*/
public void fire() throws IllegalActionException {
super.fire();
try {
long start = System.currentTimeMillis();
// load metadata and data to ecogrid
String sessionId = loginEcoGrid(authenURL, metadataUserName,
metadataPasswd);
uploadDataFile(metadataDestination, localDataFileName, dataDocid, sessionId);
uploadMetadata(metadataDestination, metadataContent, metadataDocid,
sessionId);
long end = System.currentTimeMillis();
System.out.println("**********EcogridWriter uploading data and metadata took "+(end-start)+" ms.");
// output metadata docid and data docid
TypedIOPort pp = (TypedIOPort) this.getPort(METADATADOCIDPORT);
pp.send(0, new StringToken(metadataDocid));
TypedIOPort pp1 = (TypedIOPort) this.getPort(DATADOCIDPORT);
pp1.send(0, new StringToken(dataDocid));
metadataDocid = null;
dataDocid = null;
} catch (Exception e) {
throw new IllegalActionException(e.getMessage());
}
}
/*
* This method will operate login action and return a session id
*/
private String loginEcoGrid(String authernURL, String userName,
String password) throws Exception {
String sessionId = null;
AuthenticationServiceClient client = new AuthenticationServiceClient(
authernURL);
sessionId = client.login_action(userName, password);
if (isDebugging) {
log.debug("The session id is " + sessionId);
}
// client.destory();
return sessionId;
}
/*
* Method to upload data
*/
private void uploadDataFile(String destURL, String localFileName,
String docid, String sessionId) throws Exception {
int type = EcogridObjType.DATA;
// client.createEcoGridPutLevelOnePortType();
byte[] data;
if(localData != null)
{
data = localData;
}
else
{
data = StaticUtil.getBytesArrayFromFile(localFileName);
}
if(data.length < 10)
{
System.out.println("WARNING: read " + data.length + " bytes in data file");
}
boolean error = true;
int tries = 5;
while(tries > 0 && error)
{
tries--;
error = false;
PutServiceClient client = new PutServiceClient(destURL);
try
{
client.put(data, docid, type, sessionId);
}
catch(Exception e)
{
System.out.println("data exception: " + e.getMessage());
if(tries > 0)
{
error = true;
}
else
{
throw e;
}
}
}
// client.destroy();
}
/*
* Method to upload metadata
*/
private void uploadMetadata(String destURL, String metadataContent,
String docid, String sessionId) throws Exception {
int type = EcogridObjType.METADATA;
byte[] content = metadataContent.getBytes();
if(content.length < 10)
{
System.out.println("WARNING: read " + content.length + " bytes in metadata file");
}
PutServiceClient client = new PutServiceClient(destURL);
// client.createEcoGridPutLevelOnePortType();
try { client.put(content, docid, type, sessionId); }
catch(Exception e) {
System.out.println("metadata exception: " + e.getMessage());
throw e;
}
// client.destroy();
}
/*
* After genarte docid for data file, the original metadata need to replace
* the distribution url by new value. Currently we just consider eml as
* metadata
*/
private String replaceDistributionURLAndPackageID(String originalMetadata, String newURL, String newMetadataID)
throws Exception {
String newMetadata = null;
if (originalMetadata != null) {
DocumentBuilder parser = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
// factory.setNamespaceAware(true);
parser = factory.newDocumentBuilder();
if (parser == null) {
throw new Exception("Could not create Document parser in "
+ "EcogridWriter");
}
} catch (ParserConfigurationException pce) {
throw new Exception("Could not create Document parser in "
+ "EcogridWriter: " + pce.getMessage());
}
log.debug("after generate dom parser");
Document doc = null;
StringReader reader = new StringReader(originalMetadata);
InputSource in = new InputSource(reader);
log.debug("after generate inputsource");
doc = parser.parse(in);
log.debug("after parsing inputsource");
// we assuming the metadata only have one entity
String tablePath = Eml200Parser.TABLEENTITY + "/"
+ DISTRIBUTIONPATH;
NodeList tableNodeList = XPathAPI.selectNodeList(doc, tablePath);
String rasterPath = Eml200Parser.SPATIALRASTERENTITY + "/"
+ DISTRIBUTIONPATH;
NodeList rasterNodeList = XPathAPI.selectNodeList(doc, rasterPath);
String vectorPath = Eml200Parser.SPATIALVECTORENTITY + "/"
+ DISTRIBUTIONPATH;
NodeList vectorNodeList = XPathAPI.selectNodeList(doc, vectorPath);
String procedurePath = Eml200Parser.STOREDPROCEDUREENTITY + "/"
+ DISTRIBUTIONPATH;
NodeList procedureNodeList = XPathAPI.selectNodeList(doc,
procedurePath);
String viewPath = Eml200Parser.VIEWENTITY + "/" + DISTRIBUTIONPATH;
NodeList viewNodeList = XPathAPI.selectNodeList(doc, viewPath);
String otherEntityPath = Eml200Parser.OTHERENTITY + "/"
+ DISTRIBUTIONPATH;
NodeList otherEntityNodeList = XPathAPI.selectNodeList(doc,
otherEntityPath);
if (tableNodeList != null && tableNodeList.getLength() != 0) {
// have tableEntity
log.debug("in data table path for replacement");
setNewValueForNode(tableNodeList, newURL);
} else if (rasterNodeList != null
&& rasterNodeList.getLength() != 0) {
setNewValueForNode(rasterNodeList, newURL);
} else if (vectorNodeList != null
&& vectorNodeList.getLength() != 0) {
setNewValueForNode(vectorNodeList, newURL);
} else if (procedureNodeList != null
&& procedureNodeList.getLength() != 0) {
setNewValueForNode(procedureNodeList, newURL);
} else if (viewNodeList != null && viewNodeList.getLength() != 0) {
setNewValueForNode(viewNodeList, newURL);
} else if (otherEntityNodeList != null
&& otherEntityNodeList.getLength() != 0) {
setNewValueForNode(otherEntityNodeList, newURL);
}
//replace package id
String packagePath= "/*[local-name() = '"+Eml200Parser.EML+"']/@"+Eml200Parser.PACKAGEID;
NodeList packageIDNodeList = XPathAPI.selectNodeList(doc, packagePath);
setNewValueForAttribute(packageIDNodeList, newMetadataID);
// serialize the DOM tree
StringWriter writer = new StringWriter();
XMLSerializer serializer = new XMLSerializer();
serializer.setOutputCharStream(writer);
// serializer.setOutputByteStream(System.out);
serializer.serialize(doc);
newMetadata = writer.toString();
// writer.write(newMetadata);
log.debug("The new metadata with new data reference is \n"
+ newMetadata);
}
return newMetadata;
}
/*
* Docid will look like suffix.id.rev. suffix and rev will be passed as a
* parameter, but id will generate in this method. In order to make sure the
* id is unique, it will generated by Date object.
*/
private String generateDocId(String suffix, int rev) {
//double random = Math.random();
//int randomInt = (new Double(random*100)).intValue();
String docid = null;
//Date currentTime = new Date();
//String id = Long.toString(currentTime.getTime());
//id= id+randomInt;
String id = UUID.randomUUID().toString();
docid = suffix + SEPERATOR + id + SEPERATOR + rev;
log.debug("The generated docid is " + docid);
return docid;
}
/*
* This method will set up new value for the list. We only replace the first
* one
*/
private void setNewValueForNode(NodeList list, String newValue) {
Node cn = list.item(0).getFirstChild();
if ((cn != null) && (cn.getNodeType() == Node.TEXT_NODE)) {
log.debug("set new value " + newValue + " for distribution url");
cn.setNodeValue(newValue);
}
}
/*
* Set a new value for an attribute node.
*/
private void setNewValueForAttribute(NodeList list, String newValue){
if(list != null && list.getLength() >0){
Node cn = list.item(0);
if(cn != null && cn.getNodeType() == Node.ATTRIBUTE_NODE){
//System.out.println("Set new value "+newValue +" for attribute"+cn.getNodeName());
cn.setNodeValue(newValue);
}
}
}
}