org.ecoinformatics.seek.ecogrid.EcogridWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.ecoinformatics.seek.ecogrid.EcogridWriter.java

Source

/*
 * Copyright (c) 2003-2012 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: barseghian $'
 * '$Date: 2012-08-18 00:37:31 -0700 (Sat, 18 Aug 2012) $' 
 * '$Revision: 30476 $'
 * 
 * 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;

/**
 * The EcogridWriter actor writes a data file and the EML metadata describing that
 * data file to a remote EcoGrid repositiory. Identifiers for the data and
 * metadata are created and these IDs are sent to the output ports. These IDs
 * might be used for future access to the data and metadata files. Note also
 * that the ID of the data file is inserted into the metadata file as a
 * reference (i.e., a pointer from the metadata to the data).
 * 
 * The EcoGrid is a distributed network providing scientists access to
 * ecological, biodiversity, and environmental data and analytic resources. The
 * EcoGrid can be used to store ecological data, or to model or analyze it via
 * remote EcoGrid services.
 * 
 * Ecological Metadata Language (EML) is a standard set of terms and definitions
 * used to describe ecological data. For example, EML metadata might contain
 * infomation about a data set's units of measurement, date of collection,
 * location, etc.
 * 
 * @author tao
 */
public class EcogridWriter extends TypedAtomicActor {

    /**
     * Accepts the file name and path of the local data file to upload to the EcoGrid service.
     * WARNING: This port is ignored if you supply data to dataPort.
     */
    public TypedIOPort sourceFileNamePort = null;

    /**
     * Can be used in lieu of supplying a file name to dataFileNamePort.
     * The byte[] of the input object will be uploaded.
     */
    public TypedIOPort sourceDataPort;

    /**
     * Accepts a string of metadata describing the data file. This string will be uploaded to Ecogrid service as metadata
     */
    public TypedIOPort metadataPort = null;

    /**
     * An output port that broadcasts the metadata doc ID, which is generated by the actor for future reference.
     */
    public TypedIOPort metadataDocidPort = null;

    /**
     * Broadcasts the data docid, which is 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 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",
                "<svg>\n" + "<rect x=\"-25\" y=\"-20\" " + "width=\"50\" height=\"40\" "
                        + "style=\"fill:white\"/>\n" + "<polygon points=\"-15,-10 -12,-10 -8,-14 -1,-14 3,-10"
                        + " 15,-10 15,10, -15,10\" " + "style=\"fill:red\"/>\n" + "</svg>\n");
    }

    // /////////////////////////////////////////////////////////////////
    // // public methods ////

    /**
     * If the specified attribute is <i>fileOrURL </i> and there is an open file
     * being read, then close that file and open the new one; if the attribute
     * is <i>numberOfLinesToSkip </i> and its value is negative, then throw an
     * exception. In the case of <i>fileOrURL </i>, 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 <i>fileOrURL </i> and the
     *                file cannot be opened, or the previously opened file
     *                cannot be closed; or if the attribute is
     *                <i>numberOfLinesToSkip </i> 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.
     * 
     * @param attribute
     * @return
     * @throws IllegalActionException
     */
    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 do login action and return a session id.
     * 
     * @param authernURL
     * @param userName
     * @param password
     * @return
     * @throws Exception
     */
    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;
    }

    /**
     * Upload Data. 
     * 
     * If localData is not null, localFileName
     * is ignored, you can set it null.
     * 
     * @param destURL
     * @param localFileName
     * @param docid
     * @param sessionId
     * @throws Exception
     */
    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();
    }

    /**
     * Upload Metadata.
     * 
     * @param destURL
     * @param metadataContent
     * @param docid
     * @param sessionId
     * @throws Exception
     */
    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 generate docid for data file, the original metadata need to replace
     * the distribution url by new value. Currently we just consider eml as
     * metadata.
     * 
     * @param originalMetadata
     * @param newURL
     * @param newMetadataID
     * @return
     * @throws Exception
     */
    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. 
     * Where id is a concat of currenttTimeMillis + random UUID
     * 
     * @param suffix
     * @param rev
     * @return
     */
    private String generateDocId(String suffix, int rev) {
        //double random = Math.random();
        //int randomInt = (new Double(random*1000000)).intValue();
        String docid = null;
        //Date currentTime = new Date();
        //String id = Long.toString(currentTime.getTime());
        //String id = Long.toString(System.currentTimeMillis());
        //id= id+randomInt;
        String id = Long.toString(System.currentTimeMillis());
        id = id.concat(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.
     * 
     * @param list
     * @param newValue
     */
    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.
     * 
     * @param list
     * @param newValue
     */
    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);
            }
        }
    }

}