org.kepler.actor.rest.RESTService.java Source code

Java tutorial

Introduction

Here is the source code for org.kepler.actor.rest.RESTService.java

Source

/*
 * Copyright (c) 2009-2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: crawl $'
 * '$Date: 2012-11-26 14:19:36 -0800 (Mon, 26 Nov 2012) $' 
 * '$Revision: 31113 $'
 * 
 * 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.kepler.actor.rest;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.MultipartPostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import ptolemy.actor.IOPort;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.actor.TypedIOPort;
import ptolemy.actor.parameters.PortParameter;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.expr.Parameter;
import ptolemy.data.type.BaseType;
import ptolemy.data.type.Type;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;

//////////////////////////////////////////////////////////////////////////
////RESTService
/**
 * <p>
 * The RESTService actor provides the user with a plug-in interface to execute
 * any REST web service. Given a URL for the web service and type of the service
 * GET or POST. It works with both type of servers implementing REST standards
 * or not.
 * </p>
 * <p>
 * User shall provide all the parameters that server is expecting either through
 * user configured ports or through actor defined paramInputPort. For the user
 * defined port(s), port name is the parameter name and its value is parameter
 * value. Parameters provided through actor defined paramInputPort should have
 * name & value separated by = and each name value pair should be separated by a
 * comma. Same thing holds true for actor defined fileInputPort, parameter name
 * and the file path are separated by a = and name value pairs are separated by
 * a user defined delimiter(default value is comma. This port is not read for
 * the GET service.
 * </p>
 * <p>
 * <li>Please double-click on the actor to start customization.</li>
 * <li>To enter a URL for the service URLs, select from "methodtype" dropdown
 * and select GET or POST as appropriate.</li>
 * <li>After you select the URL and methodType, "Commit" .</li></ul> </i>
 * </p>
 *
 * @author Madhu, SDSC
 * @version $Id: RESTService.java 31113 2012-11-26 22:19:36Z crawl $
 */
public class RESTService extends TypedAtomicActor {

    private static final long serialVersionUID = 1L;
    private static Log log = LogFactory.getLog(RESTService.class);

    private static final String GET = "Get";
    private static final String POST = "Post";

    private String parDelimiter = null;
    private StringBuilder messageBldr = null;

    /** Output of REST service. */
    public TypedIOPort outputPort;

    /** Input file. */
    public TypedIOPort fileInputPort;

    /** Input parameters to REST service. */
    public TypedIOPort paramInputPort;

    /** The REST service URL. */
    public PortParameter serviceSiteURL;

    /** The REST service method to use, either GET or POST. */
    public Parameter methodType;

    /** The REST service method to use, either GET or POST. */
    public Parameter delimiter;

    /**
     * Construct an actor with the given container and name. In addition to
     * invoking the base class constructors, construct the <i>serviceSiteURL</i>
     * and <i>methodType</i> parameter. Initialize <i>serviceSiteURL</i> to the
     * URL of the site offering the web services. It is needed and must start
     * with "http://". <i>methodType</i> is a drop down for the type of the
     * request this service is accepting, which could be Get or Post.
     *
     * @param container
     *            The container.
     * @param name
     *            The name of this actor.
     * @exception IllegalActionException
     *                If the actor cannot be contained by the proposed
     *                container.
     * @exception NameDuplicationException
     *                If the container already has an actor with this name.
     */
    public RESTService(CompositeEntity container, String name)
            throws NameDuplicationException, IllegalActionException {

        super(container, name);

        // serviceSiteURL = new Parameter(this, "serviceSiteURL");
        serviceSiteURL = new PortParameter(this, "serviceSiteURL");
        serviceSiteURL.setExpression("");
        serviceSiteURL.setStringMode(true);

        methodType = new Parameter(this, "methodType");
        methodType.setExpression("");
        methodType.setStringMode(true);
        methodType.addChoice(GET);
        methodType.addChoice(POST);

        delimiter = new Parameter(this, "Provide delimiter");
        delimiter.setExpression(ServiceUtils.PARAMDELIMITER);
        delimiter.setStringMode(true);

        outputPort = new TypedIOPort(this, "outputPort", false, true);
        outputPort.setTypeEquals(BaseType.STRING);

        paramInputPort = new TypedIOPort(this, "paramInputPort", true, false);
        paramInputPort.setTypeEquals(BaseType.STRING);

        fileInputPort = new TypedIOPort(this, "fileInputPort", true, false);
        fileInputPort.setTypeEquals(BaseType.STRING);

    }

    /**
     * It returns the URL provided as a value of <i> serviceSiteURL </i>
     * Parameter for RESTService actor as a String.
     *
     * @return ServiceSiteURL as String
     */
    private String getServiceSiteURL() throws IllegalActionException {

        return ((StringToken) serviceSiteURL.getToken()).stringValue().trim();

    }

    /**
     * It works only on user defined/configured ports and excludes actor defined
     * ports.
     *
     * @return List of Name, Value pairs of port names and values
     */
    private List<NameValuePair> getPortValuePairs() throws IllegalActionException {

        List<NameValuePair> nvList = new ArrayList<NameValuePair>();
        List inPortList = this.inputPortList();
        Iterator ports = inPortList.iterator();

        while (ports.hasNext()) {
            IOPort p = (IOPort) ports.next();
            if (p != fileInputPort && p != paramInputPort && !p.getName().equals(serviceSiteURL.getName())) {

                String pValue = getPortValue(p);
                if (pValue != null || pValue.trim().length() != 0) {
                    nvList.add(new NameValuePair(p.getName(), pValue));
                    System.out.println("NAME: " + p.getName() + " VALUE: " + pValue);
                }

            }
        }

        return nvList;

    }

    /**
     *
     * @param iop
     * @return the value associated with a particular port
     * @throws IllegalActionException
     */

    private String getPortValue(IOPort iop) throws IllegalActionException {

        String result = null;
        if (iop.getWidth() > 0) {
            Token tk = iop.get(0);
            Type tp = tk.getType();
            if (tp == BaseType.INT || tp == BaseType.LONG || tp == BaseType.FLOAT || tp == BaseType.DOUBLE) {

                result = tk.toString().trim();
                log.debug("String value of the Token: " + result);
            } else {
                result = ((StringToken) tk).stringValue().trim();
            }
            if (_debugging) {
                _debug("In getPortValue method RESULT: " + result);
            }
        }

        return result;
    }

    /**
     *
     * @param inputValues
     *            is a comma separated values fed as input to the actor from a
     *            String Constant actor.
     * @return a List after splitting the inputValues with PARAMDELIMITER (,)
     *         defined in the ServiceUtils class
     */
    private List<String> generateInputParameterList(String inputValues) {

        String[] paramArray = inputValues.split(parDelimiter);

        List<String> pList = new ArrayList<String>();

        if (paramArray.length == 0) {
            return null;
        }
        for (String param : paramArray) {
            pList.add(param);
        }
        return pList;
    }

    /**
     * It converts List<String> to List<NameValuePair> after splitting each
     * String with EQUALDELIMITER and adding to the NameValuePair List if Split
     * String array length is 2 else reject it.
     *
     * @param valuesList
     * @return a List<NameValuePair>
     */
    private List<NameValuePair> generatePairList(List<String> valuesList) {
        List<NameValuePair> pairList = new ArrayList<NameValuePair>();
        if (valuesList.size() > 0) {

            for (String value : valuesList) {
                String[] splitString = value.split(ServiceUtils.EQUALDELIMITER);
                if (splitString.length == 2) {
                    if (!ServiceUtils.checkEmptyString(splitString[0])
                            && !ServiceUtils.checkEmptyString(splitString[1])) {
                        pairList.add(new NameValuePair(ServiceUtils.trimString(splitString[0]),
                                ServiceUtils.trimString(splitString[1])));
                    }
                }
            }

            return pairList;
        }
        return null;
    }

    private void setParDelimiter() throws IllegalActionException {

        String tmp = ((StringToken) delimiter.getToken()).stringValue().trim();

        if (!ServiceUtils.checkEmptyString(tmp)) {

            parDelimiter = tmp;
        } else {
            parDelimiter = ServiceUtils.PARAMDELIMITER;
        }
    }

    /**
     * @return
     */
    private String getParDelimiter() {
        return parDelimiter;
    }

    /**
     * Sends the results as a String back after executing the appropriate
     * service.
     */
    @Override
    public void fire() throws IllegalActionException {

        super.fire();
        serviceSiteURL.update();

        setParDelimiter();

        if (_debugging) {
            _debug("DELIMITER IS: " + parDelimiter);
        }

        String ssURL = getServiceSiteURL();

        if (ServiceUtils.checkEmptyString(ssURL)) {
            outputPort.send(0, new StringToken("Please provide URL"));
            return;
        } else if (!ssURL.startsWith("http://")) {
            outputPort.send(0, new StringToken("URL must start with http://"));
            return;
        }

        String inputP = null;
        // In case no port is connected to the paramInputPort
        if (paramInputPort.getWidth() > 0) {
            Token tkP = paramInputPort.get(0);
            if (tkP != null) {
                inputP = ((StringToken) tkP).stringValue().trim();
            }
            if (_debugging) {
                _debug("INPUT parameter: " + inputP);
            }
        }

        List<String> paramList = null;
        List<NameValuePair> paramPairList = null;

        if (!ServiceUtils.checkEmptyString(inputP)) {
            paramList = generateInputParameterList(inputP);
            if (paramList.size() > 0) {
                paramPairList = generatePairList(paramList);
            }
        }

        String type = ((StringToken) methodType.getToken()).stringValue();
        if (_debugging) {
            _debug("METHOD TYPE VALUE: " + type);
        }

        String returnedResults = null;

        if (type.equals(GET)) {
            log.debug("CALLING GET METHOD");
            returnedResults = executeGetMethod(paramPairList, ssURL);
        } else if (type.equals(POST)) {
            log.debug("CALLING POST METHOD");

            String fileParam = null;
            if (fileInputPort.getWidth() == 0) {
                returnedResults = executePostMethod(paramPairList, null, ssURL);
            } else {

                Token tkF = fileInputPort.get(0);
                fileParam = ((StringToken) tkF).stringValue().trim();

                if (_debugging) {
                    _debug("INPUT FILE parameters: " + fileParam);
                }

                List<String> fileList = null;
                List<NameValuePair> filePairList = null;

                if (!ServiceUtils.checkEmptyString(fileParam)) {
                    fileList = generateInputParameterList(fileParam);
                    if (fileList.size() > 0) {
                        filePairList = generatePairList(fileList);
                    }
                }

                returnedResults = executePostMethod(paramPairList, filePairList, ssURL);
            }
        } else {
            returnedResults = "NO WEB SERVICE CALL MADE";
            log.debug("NEITHER GET NOR POST");
        }

        messageBldr = null; // messageBldr is set to null again
        outputPort.send(0, new StringToken(returnedResults));

    }

    /**
     *
     * @param pmPairList
     *            List of the name and value parameters that user has provided
     *            through paramInputPort. However in method this list is
     *            combined with the user configured ports and the combined list
     *            name value pair parameters are added to the service URL
     *            separated by ampersand.
     * @param nvPairList
     *            List of the name and value parameters that user has provided
     * @return the results after executing the Get service.
     */
    public String executeGetMethod(List<NameValuePair> nvPairList, String serSiteURL)
            throws IllegalActionException {

        if (_debugging) {
            _debug("I AM IN GET METHOD");
        }
        //log.debug("I AM IN GET METHOD");

        HttpClient client = new HttpClient();

        StringBuilder results = new StringBuilder();
        results.append(serSiteURL);
        List<NameValuePair> fullPairList = getCombinedPairList(nvPairList);

        if (fullPairList.size() > 0) {

            results.append("?");

            int pairListSize = fullPairList.size();
            for (int j = 0; j < pairListSize; j++) {
                NameValuePair nvPair = fullPairList.get(j);
                results.append(nvPair.getName()).append(ServiceUtils.EQUALDELIMITER).append(nvPair.getValue());
                if (j < pairListSize - 1) {
                    results.append("&");
                }

            }
        }
        if (_debugging) {
            _debug("RESULTS :" + results.toString());
        }

        // Create a method instance.
        GetMethod method = new GetMethod(results.toString());
        InputStream rstream = null;
        StringBuilder resultsForDisplay = new StringBuilder();

        try {

            messageBldr = new StringBuilder();
            messageBldr.append("In excuteGetMethod, communicating with service: ").append(serSiteURL)
                    .append("   STATUS Code: ");

            int statusCode = client.executeMethod(method);
            messageBldr.append(statusCode);

            log.debug(messageBldr.toString());
            if (_debugging) {
                _debug(messageBldr.toString());
            }

            // if(statusCode == 201){
            // System.out.println("Success -- " + statusCode +
            // ServiceUtils.ANEMPTYSPACE + method.getResponseBodyAsString());
            // }else{
            // System.out.println("Failure -- " + statusCode +
            // ServiceUtils.ANEMPTYSPACE + method.getResponseBodyAsString());
            // }

            rstream = method.getResponseBodyAsStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(rstream));

            String s;
            while ((s = br.readLine()) != null) {
                resultsForDisplay.append(s).append(ServiceUtils.LINESEP);
            }

        } catch (HttpException httpe) {
            if (_debugging) {
                _debug("Fatal protocol violation: " + httpe.getMessage());
            }
            httpe.printStackTrace();
            throw new IllegalActionException(this, httpe, "Fatal Protocol Violation: " + httpe.getMessage());
        } catch (ConnectException conExp) {
            if (_debugging) {
                _debug("Perhaps service '" + serSiteURL + "' is not reachable: " + conExp.getMessage());
            }
            conExp.printStackTrace();
            throw new IllegalActionException(this, conExp,
                    "Perhaps service '" + serSiteURL + "' is not reachable: " + conExp.getMessage());
        } catch (IOException ioe) {
            if (_debugging) {
                _debug("IOException: " + ioe.getMessage());
            }
            // System.err.println("Fatal transport error: " + e.getMessage());
            ioe.printStackTrace();
            throw new IllegalActionException(this, ioe, "IOException: " + ioe.getMessage());
        } catch (Exception e) {
            if (_debugging) {
                _debug("Fatal transport error: " + e.getMessage());
            }
            // System.err.println("Fatal transport error: " + e.getMessage());
            e.printStackTrace();
            throw new IllegalActionException(this, e, "Error: " + e.getMessage());
        } finally {
            // Release the connection.
            method.releaseConnection();
            client = null;
            // close InputStream;
            if (rstream != null)
                try {
                    rstream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new IllegalActionException(this, e, "InputStream Close Exception: " + e.getMessage());
                }
        }

        return resultsForDisplay.toString();

    }

    /**
     *
     * @param nmvlPairList
     *            NameValue pair List of the parameters user has provided
     *            through paramInputPort as comma separated pairs. Pairs are
     *            separated by '='.
     * @return
     */
    public List<NameValuePair> getCombinedPairList(List<NameValuePair> nmvlPairList) throws IllegalActionException {
        List<NameValuePair> combinedPairList = new ArrayList<NameValuePair>();
        List<NameValuePair> nameValuePairList = getPortValuePairs();
        if (nameValuePairList != null && nameValuePairList.size() > 0) {
            combinedPairList.addAll(nameValuePairList);
        }
        if (nmvlPairList != null && nmvlPairList.size() > 0) {
            combinedPairList.addAll(nmvlPairList);
        }
        return combinedPairList;

    }

    /**
     * File & regular parameters are passed as two separate lists and they are
     * treated little differently. If flPairList is not null or empty then this
     * method uses Part Object else no.
     *
     * @param pmPairList
     *            List of the name and value parameters that user has provided
     * @param flPairList
     *            List of the name and value (full file path)of file parameters.
     *            It is essentially a list of files that user wishes to attach.
     * @return the results after executing the Post service.
     */
    public String executePostMethod(List<NameValuePair> pmPairList, List<NameValuePair> flPairList,
            String serSiteURL) throws IllegalActionException {

        if (_debugging) {
            _debug("I AM IN POST METHOD");
        }

        //log.debug("I AM IN POST METHOD");
        String postAddress = serSiteURL;

        HttpClient client = new HttpClient();
        MultipartPostMethod method = new MultipartPostMethod(postAddress);
        List<NameValuePair> fullNameValueList = getCombinedPairList(pmPairList);
        if (flPairList != null && flPairList.size() > 0) {
            fullNameValueList.addAll(flPairList);
            int totalSize = fullNameValueList.size();
            Part[] parts = new Part[totalSize];

            try {

                for (int i = 0; i < parts.length; i++) {

                    String nm = fullNameValueList.get(i).getName();
                    String vl = fullNameValueList.get(i).getValue();

                    if (i > totalSize - flPairList.size() - 1) {
                        System.out.println("FILE NAME: " + nm);
                        File file = getFileObject(vl);
                        // System.out.println("FILE NAME: " + file.getName());
                        parts[i] = new FilePart(nm, file);
                        method.addPart(parts[i]);
                        System.out.println("PARTS: " + i + " " + parts[i].getName());
                        System.out.println("file Name: " + vl);

                    } else {

                        System.out.println("PARAMETER NAME: " + nm);
                        System.out.println("PARAMETER Value: " + vl);
                        parts[i] = new StringPart(nm, vl);
                        method.addPart(parts[i]);

                    }
                    if (_debugging) {
                        _debug("Value of i: " + i);
                    }

                }

            } catch (FileNotFoundException fnfe) {
                if (_debugging) {
                    _debug("File Not Exception: " + fnfe.getMessage());
                }
                fnfe.printStackTrace();
                throw new IllegalActionException(this, fnfe, "File Not Found: " + fnfe.getMessage());
            }
        } else {
            for (NameValuePair nmPair : fullNameValueList) {
                method.addParameter(nmPair.getName(), nmPair.getValue());
            }
        }

        InputStream rstream = null;
        StringBuilder results = new StringBuilder();
        try {

            messageBldr = new StringBuilder();
            messageBldr.append("In excutePostMethod, communicating with service: ").append(serSiteURL)
                    .append("   STATUS Code: ");

            int statusCode = client.executeMethod(method);
            messageBldr.append(statusCode);

            log.debug("DEBUG: " + messageBldr.toString());
            System.out.println(messageBldr.toString());

            // if(statusCode == 201){
            // System.out.println("Succuess -- " + statusCode +
            // ServiceUtils.ANEMPTYSPACE + method.getResponseBodyAsString());
            // }else{
            // System.out.println("Failure -- " + statusCode +
            // ServiceUtils.ANEMPTYSPACE + method.getResponseBodyAsString());
            // }
            rstream = method.getResponseBodyAsStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(rstream));

            log.debug("BEFORE WHILE LOOP");
            String s;
            while ((s = br.readLine()) != null) {
                results.append(s).append(ServiceUtils.LINESEP);
            }

        } catch (HttpException e) {
            if (_debugging) {
                _debug("Fatal protocol violation: " + e.getMessage());
            }
            e.printStackTrace();
            return "Protocol Violation";
        } catch (ConnectException conExp) {
            if (_debugging) {
                _debug("Perhaps service '" + serSiteURL + "' is not reachable: " + conExp.getMessage());
            }
            conExp.printStackTrace();
            throw new IllegalActionException(this, conExp,
                    "Perhaps service '" + serSiteURL + "' is not reachable: " + conExp.getMessage());
        } catch (IOException ioe) {
            if (_debugging) {
                _debug("Fatal transport error: " + ioe.getMessage());
            }
            // System.err.println("Fatal transport error: " + e.getMessage());
            ioe.printStackTrace();
            throw new IllegalActionException(this, ioe, "IOException: Perhaps could not"
                    + " connect to the service '" + serSiteURL + "'. " + ioe.getMessage());
        } catch (Exception e) {
            if (_debugging) {
                _debug("Error: " + e.getMessage());
            }
            // System.err.println("Fatal transport error: " + e.getMessage());
            e.printStackTrace();
            throw new IllegalActionException(this, e, "Error: " + e.getMessage());
        } finally {
            // Release the connection.
            method.releaseConnection();
            client = null;
            // close InputStream;
            if (rstream != null)
                try {
                    rstream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new IllegalActionException(this, e, "InputStream Close Exception: " + e.getMessage());
                }
        }
        return results.toString();
    }

    /**
     *
     * @param val
     * @return the File object for the supplied String
     */
    private File getFileObject(String val) throws IllegalActionException {
        if (val.startsWith("file:")) {

            URI uri = null;
            try {
                uri = new URI(val);
            } catch (URISyntaxException synE) {
                synE.printStackTrace();
                throw new IllegalActionException(this, synE, "Error: " + synE.getMessage());
            }
            return new File(uri);
        } else {
            return new File(val);
        }
    }

    // public boolean postfire() {
    // return false;
    // }

}