org.sdm.spa.WebService.java Source code

Java tutorial

Introduction

Here is the source code for org.sdm.spa.WebService.java

Source

/*
 * Copyright (c) 2004-2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: crawl $'
 * '$Date: 2012-05-09 11:05:40 -0700 (Wed, 09 May 2012) $' 
 * '$Revision: 29823 $'
 * 
 * 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.sdm.spa;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.wsdl.Binding;
import javax.wsdl.Operation;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ServiceException;

import org.apache.axis.wsdl.gen.Parser;
import org.apache.axis.wsdl.symbolTable.BindingEntry;
import org.apache.axis.wsdl.symbolTable.Parameters;
import org.apache.axis.wsdl.symbolTable.ServiceEntry;
import org.apache.axis.wsdl.symbolTable.SymTabEntry;
import org.apache.axis.wsdl.symbolTable.SymbolTable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;

import ptolemy.actor.IOPort;
import ptolemy.actor.TypedAtomicActor;
import ptolemy.actor.TypedIOPort;
import ptolemy.actor.parameters.ParameterPort;
import ptolemy.actor.parameters.PortParameter;
import ptolemy.data.ArrayToken;
import ptolemy.data.BooleanToken;
import ptolemy.data.DoubleToken;
import ptolemy.data.IntToken;
import ptolemy.data.LongToken;
import ptolemy.data.StringToken;
import ptolemy.data.Token;
import ptolemy.data.UnsignedByteToken;
import ptolemy.data.expr.Parameter;
import ptolemy.data.expr.SingletonParameter;
import ptolemy.data.expr.StringParameter;
import ptolemy.data.type.ArrayType;
import ptolemy.data.type.BaseType;
import ptolemy.gui.GraphicalMessageHandler;
import ptolemy.kernel.CompositeEntity;
import ptolemy.kernel.Relation;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;

//////////////////////////////////////////////////////////////////////////
////WebService
/**
 * <p>
 * The WebService actor provides the user with a plug-in interface to execute
 * any WSDL-defined web service. Given a URL for the WSDL of a web service and
 * an operation name that is included in the WSDL, this actor customizes itself
 * to execute this web service operation.
 * </p>
 * <p>
 * WSDL is an XML format for describing network services as a set of endpoints
 * operating on messages containing either document-oriented or procedure-
 * oriented information. The operations and messages are described abstractly,
 * and then bound to a concrete network protocol and message format to define an
 * endpoint. Related concrete endpoints are combined into abstract endpoints
 * (services). WSDL is extensible to allow description of endpoints and their
 * messages regardless of what message formats or network protocols are used to
 * communicate. More information on WSDL and realted standard can be found at:
 * http://www.w3.org/TR/wsdl
 * </p>
 * <p>
 * The user can instantiate the generic web service actor by providing the WSDL
 * URL and choosing the desired web service operation. The actor then a
 * utomatically specializes itself and adds ports with the inputs and outputs as
 * described by the WSDL. The so instantiated actor acts as a proxy for the web
 * service being executed and links to the other actors through its ports.
 * </p>
 * <p>
 * The WSDL is parsed to get the input, output and binding information. It
 * dynamically generates ports for each input and output of the operation. This
 * customization happens at the configuration time of a model. When the actor is
 * fired at run time, it gets the binding information and creates a call object
 * to run the model. Using this call object, it invokes the web service and
 * broadcasts the response to the output ports.
 * </p>
 * <p>
 * <i><b>Notices to users:</b></i>
 * <ul>
 * <li>Please double-click on the actor to start customization.</li>
 * <li>To enter a WSDL URL which is not in the given list of WSDL URLs, click on
 * the "Preferences" button on the configuration interface and change the type
 * of the parameter to "Text". Then you can type in the WSDL you would like to
 * use.</li>
 * <li>After you select the WSDL, "Commit" and double-click on the actor again.
 * This will reconfigure the list of available operations. Please do this
 * everytime you change the WSDL URL.</li>
 * </ul>
 * </i>
 * </p>
 * 
 * @author Ilkay Altintas, Updates by: Karen L. Schuchardt, Zhiming Zhao,
 *         Daniel Crawl
 * @version $Id: WebService.java 29823 2012-05-09 18:05:40Z crawl $
 */

public class WebService extends TypedAtomicActor {

    /**
     * Construct a WebService actor with the given container and name.
     * 
     * @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 WebService(CompositeEntity container, String name)
            throws NameDuplicationException, IllegalActionException {

        super(container, name);

        wsdlUrl = new StringParameter(this, "wsdlUrl");
        // wsdlUrl.setExpression("http://xml.nig.ac.jp/wsdl/DDBJ.wsdl");
        methodName = new StringParameter(this, "methodName");
        userName = new StringParameter(this, "userName");
        password = new StringParameter(this, "password");
        timeout = new StringParameter(this, "timeout");
        timeout.setExpression("" + 600000);

        startTrigger = new TypedIOPort(this, "startTrigger", true, false);
        // new Attribute(startTrigger, "_showName");
        // startTrigger.setContainer(null);
        hide = new SingletonParameter(startTrigger, "_hide");
        hide.setToken(BooleanToken.TRUE);
        // Set the trigger Flag.
        hasTrigger = new Parameter(this, "hasTrigger", new BooleanToken(false));
        hasTrigger.setTypeEquals(BaseType.BOOLEAN);

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

        _attachText("_iconDescription", "<svg>\n" + "<rect x=\"0\" y=\"0\" " + "width=\"60\" height=\"30\" "
                + "style=\"fill:white\"/>\n" + "</svg>\n");
    }

    // /////////////////////////////////////////////////////////////////
    // // ports and parameters ////

    /**
     * The parameter for the URL of the web service WSDL.
     */
    public StringParameter wsdlUrl;
    /**
     * The parameter for the method name.
     */
    public StringParameter methodName;
    /**
     * The userName to invoke the web service if necessary.
     */
    public StringParameter userName;
    /**
     * The password to invoke the web service if necessary.
     */
    public StringParameter password;
    /**
     * The timeout duration in web service call.
     */
    public StringParameter timeout;
    /**
     * This is an parameter to activate the optional startTrigger port. Please
     * activate it <i>ONLY</i> when the actor has no input and it is required
     * for scheduling of the actor.
     */
    public Parameter hasTrigger;
    /**
     * This is an optional input port that can be used to help the scheduling of
     * the actor.
     * 
     * <p>
     * This port is activated by the hasTrigger parameter. Double-click on the
     * actor to enable. Please enable it <i>ONLY</i> when the actor has no input
     * and it is required for scheduling of the actor.
     * </p>
     */
    public TypedIOPort startTrigger;
    /**
     * It outputs the errors if any occured when actor is executing. It outputs
     * "NO ERRORS." if there are no exceptional cases.
     * 
     */
    public TypedIOPort clientExecErrors;

    public SingletonParameter hide;

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

    /**
     * Callback for changes in attribute values Get the WSDL from the given URL.
     * 
     * @param at
     *            The attribute that changed.
     * @exception IllegalActionException
     */
    public void attributeChanged(Attribute at) throws IllegalActionException {

        if (_isDebugging) {
            _log.debug("attribute changed: " + at);
        }

        if (at == hasTrigger) {
            _triggerFlag = ((BooleanToken) hasTrigger.getToken()).booleanValue();
            _debug("<TRIGGER_FLAG>" + _triggerFlag + "</TRIGGER_FLAG>");
            if (_triggerFlag) {
                hide.setToken(BooleanToken.FALSE);
            } else {
                List<?> inPortList = inputPortList();
                Iterator<?> ports = inPortList.iterator();
                while (ports.hasNext()) {
                    IOPort p = (IOPort) ports.next();
                    if (p.isInput()) {
                        if (p.getName().equals("startTrigger")) {
                            // p.setContainer(null);
                            hide.setToken(BooleanToken.TRUE);
                        }
                    }
                }
            }
        } else if (at == wsdlUrl) {

            String newUrlStr = ((StringToken) wsdlUrl.getToken()).stringValue();

            if (!_urlStr.equals(newUrlStr)) {
                _urlStr = newUrlStr;

                methodName.removeAllChoices();
                //_deletePorts();
                _wsdlParser = null;

                if (_urlStr.length() > 0) {
                    _wsdlParser = new Parser();
                    // Parse the wsdl for the web service.
                    try {
                        _wsdlParser.run(_urlStr);
                    } catch (Exception e) {
                        throw new IllegalActionException(this, e, "Could not run WSDL parser.");
                    }

                    _configureOperationNames();

                    if (_methodNameStr.length() > 0) {
                        _configureActor();
                    }
                }
            }

        } else if (at == methodName) {

            String newMethodStr = ((StringToken) methodName.getToken()).stringValue();

            if (!_methodNameStr.equals(newMethodStr)) {
                //_deletePorts();
                _methodNameStr = newMethodStr;
                _debug("<METHOD_NAME>" + _methodNameStr + "</METHOD_NAME>");

                if (_urlStr.length() > 0) {
                    int slashIndex = _urlStr.lastIndexOf('/');
                    String wsName = _urlStr.substring(slashIndex + 1, _urlStr.length() - 5);

                    _attachText("_iconDescription",
                            "<svg>\n" + "<rect x=\"0\" y=\"0\" " + "width=\"160\" height=\"50\" "
                                    + "style=\"fill:white\"/>\n" + "<text x=\"20\" y=\"25\" "
                                    + "style=\"font-size:11; fill:red; font-family:SansSerif\">" + wsName + "_"
                                    + _methodNameStr + "</text>\n" + "</svg>\n");

                    if (_methodNameStr.length() > 0 && _urlStr.length() > 0) {
                        _configureActor();
                    }
                }
            }
        }
    } // end of attributeChanged

    /** Configure the actor for the entered operation of the given web service. */
    private void _configureOperationNames() throws IllegalActionException {

        _service = null;

        // Find the entry for the ServiceEntry class in the symbol table.
        HashMap map = _wsdlParser.getSymbolTable().getHashMap();
        Iterator<?> entrySetIter = map.entrySet().iterator();
        while (entrySetIter.hasNext()) {
            final Map.Entry currentEntry = (Map.Entry) entrySetIter.next();
            final Vector valueVector = (Vector) currentEntry.getValue();
            int vecSize = valueVector.size();
            for (int index = 0; index < vecSize; ++index) {
                SymTabEntry symTabEntryObj = (SymTabEntry) valueVector.get(index);
                if ((ServiceEntry.class).isInstance(symTabEntryObj)) {
                    _service = ((ServiceEntry) symTabEntryObj).getService();
                }
            }
        }

        if (_service == null) {
            String str = _urlStr;
            _urlStr = "";
            throw new IllegalActionException(this, "Unable to find service entry for " + str);
        }

        Port port = _getSOAPAddress(_service.getPorts());
        if (port == null) {
            _log.debug("<ERROR> No port was returned by the _getSOAPAddress. </ERROR>");

        }
        _portName = "";
        _portName = port.getName();
        _binding = port.getBinding();
        SymbolTable symbolTable = _wsdlParser.getSymbolTable();
        BindingEntry bEntry = symbolTable.getBindingEntry(_binding.getQName());

        Iterator<?> iter = bEntry.getParameters().keySet().iterator();
        for (; iter.hasNext();) {
            final Operation oper = (Operation) iter.next();
            methodName.addChoice(oper.getName());
        }
    } // end-of-configureOperationNames

    /** Configure the actor for the entered operation of the given web service. */
    private void _configureActor() throws IllegalActionException {

        if (_service == null) {
            throw new IllegalActionException(this, "No service entry.");
        }

        Port port = _getSOAPAddress(_service.getPorts());
        if (port == null) {
            _debug("<ERROR> No port was returned by the _getSOAPAddress. </ERROR>");
        }
        _portName = "";
        _portName = port.getName();
        _binding = port.getBinding();
        SymbolTable symbolTable = _wsdlParser.getSymbolTable();
        BindingEntry bEntry = symbolTable.getBindingEntry(_binding.getQName());

        boolean foundOperation = false;
        Iterator<?> iter = bEntry.getParameters().keySet().iterator();
        for (; iter.hasNext();) {
            Operation operation = (Operation) iter.next();
            if (operation.getName().equals(_methodNameStr)) {
                foundOperation = true;
                final Parameters parameters = (Parameters) bEntry.getParameters().get(operation);

                _inputNameSet.clear();
                _inputNameSet.add("startTrigger");

                _outputNameSet.clear();
                _outputNameSet.add("clientExecErrors");

                //System.out.println("cleared name sets.");

                try {
                    _createPorts(parameters);
                } catch (NameDuplicationException e) {
                    throw new IllegalActionException(this, e, "Error creating ports.");
                }

                // Set output type
                if (parameters.returnParam == null) {
                    _returnMode = 1; // Get outputs into a map object!
                } else if (parameters.returnParam != null) {
                    _returnMode = 2; // Get the invoke result value as a
                                     // single value.
                                     // Get the QName for the return Type
                    QName returnQName = parameters.returnParam.getQName();
                    if (((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam).getType()
                            .getDimensions().equals("[]")) {
                        Node arrTypeNode = ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam)
                                .getType().getNode();

                        /*
                         * Original code String baseTypeStr =
                         * _getArrayBaseType(arrTypeNode);
                         */
                        /* This code updated by Zhiming Zhao */
                        String baseTypeStr = (String) ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam)
                                .getType().getRefType().getQName().getLocalPart() + "[]";
                        QName nodeQname = ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam)
                                .getType().getRefType().getQName();

                        /* End */

                        _debug("ARRAY PARAM BASE TYPE: " + baseTypeStr);

                        try {
                            _createPort(
                                    ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam).getMode(),
                                    baseTypeStr, (String) returnQName.getLocalPart());
                        } catch (NameDuplicationException e) {
                            throw new IllegalActionException(this, e, "Error creating ports.");
                        }
                    } else {
                        try {
                            _createPort(
                                    ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam).getMode(),
                                    ((org.apache.axis.wsdl.symbolTable.Parameter) parameters.returnParam).getType()
                                            .getQName().getLocalPart(),
                                    (String) returnQName.getLocalPart());
                        } catch (NameDuplicationException e) {
                            throw new IllegalActionException(this, e, "Error creating ports.");
                        }
                    }
                    _debug("<RETURN_QNAME>" + returnQName.getLocalPart() + "</RETURN_QNAME>");
                }
                // Break out of the loop
                break;
            }
        }

        if (!foundOperation) {
            String str = _methodNameStr;
            _methodNameStr = "";
            throw new IllegalActionException(this, "Could not find operation " + str);
        }

        // remove unused ports
        for (Object obj : inputPortList()) {
            IOPort curPort = (IOPort) obj;

            if (!_inputNameSet.contains(curPort.getName())) {
                //System.out.println("old input: " + curPort.getName());
                try {
                    if (curPort instanceof ParameterPort) {
                        ((ParameterPort) curPort).getParameter().setContainer(null);
                    }

                    curPort.setContainer(null);
                } catch (NameDuplicationException e) {
                    throw new IllegalActionException(this, e, "Error removing " + curPort.getName());
                }
            }
        }

        for (Object obj : outputPortList()) {
            IOPort curPort = (IOPort) obj;
            if (!_outputNameSet.contains(curPort.getName())) {
                //System.out.println("old output: " + curPort.getName());
                try {
                    curPort.setContainer(null);
                } catch (NameDuplicationException e) {
                    throw new IllegalActionException(this, e, "Error removing " + curPort.getName());
                }
            }
        }

    } // end-of-configureActor

    /** Creates ports for the web service operation */
    private void _createPorts(Parameters params) throws IllegalActionException, NameDuplicationException {
        for (int j = 0; j < params.list.size(); j++) {
            org.apache.axis.wsdl.symbolTable.Parameter param = (org.apache.axis.wsdl.symbolTable.Parameter) params.list
                    .get(j);
            _debug("PARAM DIMENSION: " + param.getType().getDimensions());
            _debug("PARAM TYPE: " + param.getType().getQName().getLocalPart());
            if (param.getType().getDimensions().equals("[]")) {

                Node arrTypeNode = param.getType().getNode();
                _debug("TYPE NAME: " + arrTypeNode.getAttributes().getNamedItem("name"));
                /*
                 * String baseTypeStr =
                 * arrTypeNode.getFirstChild().getFirstChild().
                 * getFirstChild
                 * ().getAttributes().getNamedItem("wsdl:arrayType").
                 * getNodeValue(); String[] result = baseTypeStr.split(":");
                 * baseTypeStr = result[1];
                 */
                /* Updated by Zhiming */
                String baseTypeStr = param.getType().getRefType().getQName().getLocalPart() + "[]";
                /* Updated by Zhiming End */

                _debug("ARRAY PARAM BASE TYPE: " + baseTypeStr);
                _createPort(param.getMode(), baseTypeStr, (String) param.getQName().getLocalPart());
            } else { // if (param.getType().getDimension() != null) {
                _createPort(param.getMode(), param.getType().getQName().getLocalPart(),
                        (String) param.getQName().getLocalPart());
            }
        }
    }

    /** Create a port if it does not already exist, and set its type. */
    private void _createPort(int mode, String portTypeStr, String portNameStr)
            throws IllegalActionException, NameDuplicationException {

        TypedIOPort port = null;

        // see if port already exists.
        boolean exists = false;
        port = (TypedIOPort) getPort(portNameStr);
        if (port != null) {
            if (mode == 3 || (port.isInput() && mode == 1) || (port.isOutput() && mode == 2)) {
                exists = true;
            }
        }

        // create port if necessary
        if (mode == 1) { // input
            if (!exists) {
                PortParameter parameter = new PortParameter(this, portNameStr);
                port = parameter.getPort();
                _debug("<INPUT>" + portNameStr + "</INPUT>");
            }
        } else if (mode == 2) { // output
            if (!exists) {
                port = new TypedIOPort(this, portNameStr, false, true);
                _debug("<OUTPUT>" + portNameStr + "</OUTPUT>");
            }
        } else if (mode == 3) { // input/output
            // XXX is this possible? can't have two ports w/ same name!
            if (!exists) {
                TypedIOPort pin = new TypedIOPort(this, portNameStr, true, false);
                new Attribute(pin, "_showName");
                _setPortType(pin, portTypeStr);
                _debug("<INPUT>" + portNameStr + "</INPUT>");
                port = new TypedIOPort(this, portNameStr, false, true);
                _debug("<OUTPUT>" + portNameStr + "</OUTPUT>");
            }
        }

        if (!exists) {
            // show port name
            new Attribute(port, "_showName");
        }

        // set type.
        _setPortType(port, portTypeStr);

        if (mode == 1 || mode == 3) {
            _inputNameSet.add(portNameStr);
        }

        if (mode == 2 || mode == 3) {
            _outputNameSet.add(portNameStr);
        }

    } // end-of-createPort

    /** Deletes all the ports of this actor. */
    /*
    protected void _deletePorts() throws IllegalActionException {
       List<?> inPortList = inputPortList();
       Iterator<?> ports = inPortList.iterator();
       while (ports.hasNext()) {
     IOPort port = (IOPort) ports.next();
     try {
        if (!(port.getName().equals("startTrigger"))) {
            if(port instanceof ParameterPort) {
                ((ParameterPort)port).getParameter().setContainer(null);
            }
                
           port.setContainer(null);
        }
     } catch (NameDuplicationException e) {
         throw new IllegalActionException(this, e, 
                 "Could not delete the input port: " + port.getName());
     }
       }
        
       List<?> outPortList = outputPortList();
       Iterator<?> oports = outPortList.iterator();
       while (oports.hasNext()) {
     IOPort port = (IOPort) oports.next();
     try {
        if (!(port.getName().equals("clientExecErrors"))) {
            port.setContainer(null);
        }
     } catch (NameDuplicationException e) {
        throw new IllegalActionException(this, e, 
                "Could not delete the output port: " + port.getName());
     }
       }
    } // end of deletePorts
    */

    /**
     * Get the URL address of the the location of the web service defined by the
     * given WSDL. Get the the namespace and binding information of the web
     * service using the given WSDL and methodName. Add a parameter to the call
     * object for each input part.Fill these parameters with the input values on
     * channels on the ports that correspond to them. Invoke the web service
     * using all the gathered information. Send the response of the call to the
     * result port.
     * 
     * @exception IllegalActionException
     *                If there is no director.
     */
    public void fire() throws IllegalActionException {

        super.fire();

        // triggerring the actor..
        if (startTrigger.getWidth() > 0) {
            for (int i = 0; i < startTrigger.getWidth(); i++)
                startTrigger.get(i);
        }

        _urlStr = ((StringToken) wsdlUrl.getToken()).stringValue();
        _methodNameStr = ((StringToken) methodName.getToken()).stringValue();

        _wsdlParser = new Parser();

        try {
            _wsdlParser.run(new String(_urlStr));
        } catch (Exception ex) {
            _debug("<EXCEPTION> There was an error while parsing the WSDL in fire for URL: " + _urlStr + " .\n" + ex
                    + ". </EXCEPTION>");
            // GraphicalMessageHandler.message(
            _errorsStr += "\n" + ex.getMessage()
                    + "Error in fire: There was an error while parsing the WSDL in the actor: " + this.getName();// );

            /*
             * The following exception is thrown and for the case when the web
             * service's server is down. The director SDF4WS catches the
             * exception thrown below and re-tries to get web service
             * access.After three re-trials the director finally switches over
             * to the same service but at a different server (if second server
             * is available)
             */

            GraphicalMessageHandler.message("\nWebService WSDL:" + _urlStr + " Not Responding");
            throw new IllegalActionException("\nWebService WSDL Not Responding.");

        }
        _getServiceBinding();
        try {
            SymbolTable symbolTable = _wsdlParser.getSymbolTable();
            BindingEntry bEntry = symbolTable.getBindingEntry(_binding.getQName());
            Parameters parameters = null;
            Iterator<?> iter = bEntry.getParameters().keySet().iterator();
            for (; iter.hasNext();) {
                final Operation operation = (Operation) iter.next();
                if (operation.getName().equals(_methodNameStr)) {
                    parameters = (Parameters) bEntry.getParameters().get(operation);
                    // Set output type
                    if (parameters.returnParam == null) {
                        _returnMode = 1; // Get outputs into a map object!
                    } else if (parameters.returnParam != null) {
                        _returnMode = 2; // Get the invoke result value as a
                                         // single value.
                    }
                    // Break out of the loop
                    break;
                }
            }
        } catch (Exception ex) {
            _debug("<EXCEPTION>In fire when setting the return mode: " + ex + ". </EXCEPTION>");
            // GraphicalMessageHandler.message(
            _errorsStr += "\n" + ex.getMessage() + "There was an error when setting up the return mode of the web "
                    + "service at: " + this.getName();// );
        }

        try {
            org.apache.axis.client.Service myServiceClient = new org.apache.axis.client.Service(_wsdlParser,
                    _service.getQName());
            _call = myServiceClient.createCall(QName.valueOf(_portName), QName.valueOf(_methodNameStr));
            _debug(_call.getClass().getName() + "Call implementation");

            // KLS((org.apache.axis.client.Call) _call).setTimeout(new
            // Integer(600000));
            ((org.apache.axis.client.Call) _call).setTimeout(new Integer(timeout.stringValue()));

            // Add a parameter to the call object for each input part.
            List<?> inPortList = inputPortList();
            int numInPorts = inPortList.size();
            // _debug("<TRIGGER_FLAG_BEFORE_OBJECT_ARR>" + _triggerFlag +
            // "</TRIGGER_FLAG_BEFORE_OBJECT_ARR>");
            // if (_triggerFlag) {
            _objArr = new Object[numInPorts - 1];
            // }
            // else {
            // _objArr = new Object[numInPorts];
            // }
            Iterator<?> ports = inPortList.iterator();
            int i = 0;
            while (ports.hasNext()) {
                // Fill these parameters with the input values on channels on
                // the
                // ports that correspond to them.
                TypedIOPort p = (TypedIOPort) ports.next();
                _debug("<INPUT_PORT>" + p.getName() + "</INPUT_PORT>");
                if (p.getName().equals("startTrigger")) {
                    _debug("Skipped the value of the trigger port in fire.");
                } else {

                    PortParameter input = ((ParameterPort) p).getParameter();
                    input.update();
                    _objArr[i] = _getObjectFromToken(input.getToken());
                    i++;
                }
            }
            for (int j = 0; j < i; j++) {
                _debug("_objArr[" + j + "]=" + _objArr[j]);
            }

            _debug("<OBJ_ARR_LENGTH> " + (new Integer(_objArr.length)).toString() + " </OBJ_ARR_LENGTH>");
            _debug("<USERNAME> " + userName.stringValue() + " </USERNAME>");
            _call.setProperty(Call.USERNAME_PROPERTY, userName.stringValue());
            _debug("<PASSWORD> " + password.stringValue() + " </PASSWORD>");
            _call.setProperty(Call.PASSWORD_PROPERTY, password.stringValue());

            _debug("Starting the invoke!");
            // Element invokeResult = (Element) call.invoke(objArr);
            Object invokeResult = _call.invoke(_objArr);
            _debug("Got results from the invoke...");
            List<?> outPortList = this.outputPortList();
            Iterator<?> oports = outPortList.iterator();
            _debug("<RETURN_MODE> " + new Integer(_returnMode).toString() + " </RETURN_MODE>");
            if (_returnMode == 2) {
                while (oports.hasNext()) {
                    TypedIOPort po = (TypedIOPort) oports.next();
                    if (!(po.getName().equals("clientExecErrors"))) {
                        _sendOutput(po, invokeResult);
                    }
                }
            } else if (_returnMode == 1) {
                Map outParams = _call.getOutputParams();
                while (oports.hasNext()) {
                    // Fill these parameters with the input values on
                    // channels on the ports that correspond to them.
                    TypedIOPort po = (TypedIOPort) oports.next();
                    _debug("<OUTPUT_PORT>" + po.getName() + ", " + po.getType().toString() + "</OUTPUT_PORT>");
                    try {
                        if (!(po.getName().equals("clientExecErrors"))) {
                            _sendOutput(po, outParams.get(new QName("", po.getName())));
                        }
                    } catch (Exception ex) {
                        _debug("<EXCEPTION> There was an exception when sending the outputs." + " OutValue: "
                                + (String) org.apache.axis.utils.JavaUtils
                                        .convert(outParams.get(new QName("", po.getName())), java.lang.String.class)
                                + ". </EXCEPTION>");
                        // GraphicalMessageHandler.message(
                        _errorsStr += "\n" + ex.getMessage() + "\nThe error occured in the actor: " + this.getName()
                                + "\n Please look at the debugging details for this actor for "
                                + "more information.";// );
                    }
                }
            }
        } catch (ServiceException se) {
            throw new IllegalActionException(this, se, "Service error.");
        } catch (java.rmi.RemoteException rex) {
            throw new IllegalActionException(this, rex, "Web service error.");
        }

        System.out.println(_errorsStr);
        if (!(_errorsStr.equals(""))) {
            clientExecErrors.broadcast(new StringToken(_errorsStr));
        } else {
            clientExecErrors.broadcast(new StringToken("NO ERRORS."));
        }
    } // end of fire

    /** Configure the service, port and binding info. */
    private void _getServiceBinding() throws IllegalActionException {
        try {
            _service = null;

            // Find the entry for the ServiceEntry class in the symbol table.
            HashMap map = _wsdlParser.getSymbolTable().getHashMap();
            Iterator entrySetIter = map.entrySet().iterator();
            while (entrySetIter.hasNext()) {
                Map.Entry currentEntry = (Map.Entry) entrySetIter.next();
                Vector valueVector = (Vector) currentEntry.getValue();
                int vecSize = valueVector.size();
                for (int index = 0; index < vecSize; ++index) {
                    SymTabEntry symTabEntryObj = (SymTabEntry) valueVector.get(index);
                    if ((ServiceEntry.class).isInstance(symTabEntryObj)) {
                        _service = ((ServiceEntry) symTabEntryObj).getService();
                    }
                }
            }

            Port port = _getSOAPAddress(_service.getPorts());
            if (port == null) {
                _debug("<ERROR> No port was returned by the _getSOAPAddress. </ERROR>");
            }
            _portName = "";
            _portName = port.getName();
            _binding = port.getBinding();
        } catch (Exception ex) {
            _debug("<EXCEPTION> There was an error when configuring the actor: " + ex + ". </EXCEPTION>");
            throw new IllegalActionException(this, ex, "There was an error configuring the actor.");
        }
    } // end-of-getServiceBinding

    public void preinitialize() throws IllegalActionException {
        // convert existing input ports that are not port parameters
        // to port parameters.

        Set<IOPort> portsToChange = new HashSet<IOPort>();

        List<?> inputList = inputPortList();
        for (Object obj : inputList) {
            IOPort port = (IOPort) obj;
            if (!port.getName().equals("startTrigger") && !(port instanceof ParameterPort)) {
                portsToChange.add(port);
            }
        }

        for (IOPort port : portsToChange) {
            // get any connections to this port
            List<?> relationList = port.linkedRelationList();
            String name = port.getName();
            try {
                port.setContainer(null);
            } catch (NameDuplicationException e) {
                throw new IllegalActionException(this, e, "Error removing old port " + name);
            }

            PortParameter parameter;
            try {
                parameter = new PortParameter(this, name);
            } catch (NameDuplicationException e) {
                throw new IllegalActionException(this, e, "Error creating port parameter " + name);
            }

            port = parameter.getPort();
            for (Object obj : relationList) {
                port.link((Relation) obj);
            }

        }

        // NOTE: call parent's preinitialize last since the ports may have
        // changed.
        super.preinitialize();
    }

    // ////////////////////////////////////////////////////////////////////
    // // private methods ////

    /**
     * Returns a port with a SOAPAddress extensibility element.
     */
    private Port _getSOAPAddress(Map ports) {
        Iterator<?> nameIter = ports.keySet().iterator();
        while (nameIter.hasNext()) {
            String portName = (String) nameIter.next();
            Port port = (Port) ports.get(portName);
            List<?> extElemList = port.getExtensibilityElements();
            for (int i = 0; (extElemList != null) && (i < extElemList.size()); i++) {
                Object extEl = extElemList.get(i);
                if (extEl instanceof SOAPAddress) {
                    return port;
                }
            }
        }
        return null;
    } // end-of-getSOAPAddress

    /**
     * _sendOutput Send the output ports from the given port with the right type
     * casting.
     * 
     * @param outPort
     * @param res
     */
    private void _sendOutput(TypedIOPort outPort, Object res) {
        _debug("<RES_CLASS_NAME>" + res.getClass().getName() + "</RES_CLASS_NAME>");
        try {
            if (res instanceof String) {
                if (outPort.getType().toString().equals("string")) {
                    outPort.broadcast(new StringToken((String) res));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(String) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Integer) {
                if (outPort.getType().toString().equals("int")) {
                    outPort.broadcast(new IntToken(((Integer) res).intValue()));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Integer) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Double) {
                if (outPort.getType().toString().equals("double")) {
                    outPort.broadcast(new DoubleToken(((Double) res).doubleValue()));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Double) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Long) {
                if (outPort.getType().toString().equals("long")) {
                    outPort.broadcast(new LongToken(((Long) res).longValue()));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Long) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Boolean) {
                if (outPort.getType().toString().equals("boolean")) {
                    outPort.broadcast(new BooleanToken(((Boolean) res).booleanValue()));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Boolean) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof String[]) {
                if (outPort.getType().toString().equals("{string}")) {
                    String[] resultArr = (String[]) res;
                    int xxx = resultArr.length;

                    /*
                     * Original code String resultArrStr = "{"; for (int
                     * resCount = 0; resCount < xxx - 1; resCount++) {
                     * _debug("resultArr[" + resCount + "] = " +
                     * resultArr[resCount]); resultArrStr += resultArr[resCount]
                     * + ", ";
                     * 
                     * } resultArrStr += resultArr[xxx - 1] + "}";
                     */
                    /* code updated by zhiming */

                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);

                        /*
                         * Check if '"' is included in the string array.
                         */
                        if (((resultArr[resCount].getBytes())[0] == (resultArr[resCount]
                                .getBytes())[resultArr[resCount].length() - 1])
                                && (resultArr[resCount].getBytes()[0] == 34)) {
                            resultArrStr += resultArr[resCount] + ",";
                            // System.out.println("Without adding \"");;

                        } else {
                            resultArrStr += "\"" + resultArr[resCount] + "\",";
                            // System.out.println("add \"");;
                        }
                    }
                    if (((resultArr[xxx - 1].getBytes())[0] == (resultArr[xxx - 1].getBytes())[resultArr[xxx - 1]
                            .length() - 1]) && ((resultArr[xxx - 1].getBytes())[0] == 34)) {
                        resultArrStr += resultArr[xxx - 1] + "}";
                        // System.out.println("Without adding \"");;
                    } else {
                        resultArrStr += "\"" + resultArr[xxx - 1] + "\"}";
                        // System.out.println("add \"");;
                    }
                    // System.out.println("stringArrayB: "+resultArrStr);;
                    /* End Zhiming */

                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(String[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if ((res instanceof Integer[])) {
                _debug("IN Integer[]");
                if (outPort.getType().toString().equals("{int}")) {
                    Integer[] resultArr = (Integer[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(String[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof int[]) {
                _debug("IN int[]");
                if (outPort.getType().toString().equals("{int}")) {
                    int[] resultArr = (int[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(String[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Double[]) {
                _debug("IN Double[]");
                if (outPort.getType().toString().equals("{double}")) {
                    Double[] resultArr = (Double[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Double[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof double[]) {
                _debug("IN double[]");
                if (outPort.getType().toString().equals("{double}")) {
                    double[] resultArr = (double[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(double[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Float[]) {
                _debug("IN Float[]");
                if (outPort.getType().toString().equals("{double}")) {
                    Float[] resultArr = (Float[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Float[]) do not match in _sendOutput(). <ERROR>");
                }
            }

            else if (res instanceof float[]) {
                _debug("IN float[]");
                if (outPort.getType().toString().equals("{double}")) {
                    float[] resultArr = (float[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(float[]) do not match in _sendOutput(). <ERROR>");
                }
            } else if (res instanceof Boolean[]) {
                _debug("IN Boolean[]");
                if (outPort.getType().toString().equals("{boolean}")) {
                    Boolean[] resultArr = (Boolean[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(Boolean[]) do not match in _sendOutput(). <ERROR>");
                }
            }

            else if (res instanceof boolean[]) {
                _debug("IN boolean[]");
                if (outPort.getType().toString().equals("{boolean}")) {
                    boolean[] resultArr = (boolean[]) res;
                    int xxx = resultArr.length;
                    String resultArrStr = "{";
                    for (int resCount = 0; resCount < xxx - 1; resCount++) {
                        _debug("resultArr[" + resCount + "] = " + resultArr[resCount]);
                        resultArrStr += resultArr[resCount] + ", ";
                    }
                    resultArrStr += resultArr[xxx - 1] + "}";
                    _debug(resultArrStr);
                    outPort.broadcast(new ArrayToken(resultArrStr));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(boolean[]) do not match in _sendOutput(). <ERROR>");
                }
            }

            else if (res instanceof byte[]) {
                _debug("IN byte[]");
                if (outPort.getType().toString().equals("{unsignedByte}")) {
                    byte[] resultArr = (byte[]) res;
                    int bytesAvailable = resultArr.length;
                    Token[] dataTokens = new Token[bytesAvailable];

                    for (int j = 0; j < bytesAvailable; j++) {
                        dataTokens[j] = new UnsignedByteToken(resultArr[j]);
                    }

                    outPort.broadcast(new ArrayToken(dataTokens));
                } else {
                    _debug("<ERROR> The outPort type (" + outPort.getType().toString()
                            + ") and the res type(boolean[]) do not match in _sendOutput(). <ERROR>");
                }
            }

            else
                outPort.broadcast(new StringToken("Cannot identify the type instance of the result!"));
        } catch (IllegalActionException iae) {
            _debug("<EXCEPTION> There was an exception in _sendOutput(): " + iae.toString() + ". </EXCEPTION>");
            // GraphicalMessageHandler.message(
            _errorsStr += "\n" + iae.getMessage() + "There was an exception when sending the outputs in actor: "
                    + this.getName() + iae.toString();// );
        }
    } // end-of-sendOutput

    /** Get the native object from a Token. */
    private Object _getObjectFromToken(Token token) {
        if (token instanceof DoubleToken) {
            return ((DoubleToken) token).doubleValue();
        } else if (token instanceof IntToken) {
            return ((IntToken) token).intValue();
        } else if (token instanceof StringToken) {
            return ((StringToken) token).stringValue();
        } else if (token instanceof LongToken) {
            return ((LongToken) token).longValue();
        } else if (token instanceof BooleanToken) {
            return ((BooleanToken) token).booleanValue();
        } else if (token instanceof ArrayToken) {
            ArrayToken arrayToken = (ArrayToken) token;
            Object[] arrayObj = new Object[arrayToken.length()];
            for (int i = 0; i < arrayToken.length(); i++) {
                arrayObj[i] = _getObjectFromToken(arrayToken.getElement(i));
            }
            return arrayObj;
        } else {
            System.out.println("WARNING: unknown type of token: " + token.getType());
            return token.toString();
        }
    }

    /**
     * Set the type of a port based on a string representation of that type that
     * was extracted from the WSDL description.
     * 
     * @param arrayTypes
     *            a hash of defined array types by name
     * @param port
     *            the port whose type is to be set
     * @param typeStr
     *            the string representation of the type to be set
     */
    private void _setPortType(TypedIOPort port, String typeStr) {
        /*
         * NOTE TO SELF: I used:
         * http://www-106.ibm.com/developerworks/webservices
         * /library/ws-soapmap1/ as reference for the Apache->SOAP type mapping.
         * Haven't got to the special "object encoding" and "Sending blobs"
         * parts of the doc. Need to consider them seperately if we wanna do it.
         */
        if (typeStr.equals("int")) {
            port.setTypeEquals(BaseType.INT);
        } else if (typeStr.equals("boolean")) {
            port.setTypeEquals(BaseType.BOOLEAN);
        } else if (typeStr.equals("long")) {
            port.setTypeEquals(BaseType.LONG);
        } else if (typeStr.equals("double")) {
            port.setTypeEquals(BaseType.DOUBLE);
        } else if (typeStr.equals("float")) { // There is no float in Ptolemy
            // type sys.
            port.setTypeEquals(BaseType.DOUBLE);
        } else if (typeStr.equals("byte")) {
            // ->There is no byte in Ptolemy type sys. So I cast the byte to
            // INT.
            port.setTypeEquals(BaseType.INT);
        } else if (typeStr.equals("short")) {
            // ->There is no short in Ptolemy type sys. So again cast it to INT
            port.setTypeEquals(BaseType.INT);
        } else if (typeStr.equals("string")) {
            port.setTypeEquals(BaseType.STRING);
        } else if (typeStr.equals("string[]")) {
            port.setTypeEquals(new ArrayType(BaseType.STRING));
        } else if (typeStr.equals("byte[]")) {
            port.setTypeEquals(new ArrayType(BaseType.INT));
        } else if (typeStr.equals("short[]")) {
            port.setTypeEquals(new ArrayType(BaseType.INT));
        } else if (typeStr.equals("int[]")) {
            port.setTypeEquals(new ArrayType(BaseType.INT));
        } else if (typeStr.equals("long[]")) {
            port.setTypeEquals(new ArrayType(BaseType.LONG));
        } else if (typeStr.equals("double[]")) {
            port.setTypeEquals(new ArrayType(BaseType.DOUBLE));
        } else if (typeStr.equals("float[]")) {
            port.setTypeEquals(new ArrayType(BaseType.DOUBLE));
        } else if (typeStr.equals("boolean[]")) {
            port.setTypeEquals(new ArrayType(BaseType.BOOLEAN));
        } else {
            _debug("<WARNING>Could not specify the type. Setting it to string. </WARNING>");
            port.setTypeEquals(BaseType.STRING);
        }
    }

    // ////////////////////////////////////////////////////////////////////
    // // private variables ////

    private Binding _binding = null;
    // The main service call object
    private Call _call = null;
    // The name of the method that this web service actor binds to
    // static private String _methodNameStr = "";
    private String _methodNameStr = "";
    // The input values to be sent when invoking the web service call.
    private Object[] _objArr;
    // The name of the port...
    private String _portName = null;
    private int _returnMode = 0; // 1--multiple output 2--single output param
    private Service _service = null;
    // The URL of the WSDL that describes the web service
    // static private String _urlStr = new String();
    private String _urlStr = new String();
    // The parser for the WSDL. Will be initiated by the _urlStr.
    private Parser _wsdlParser = null;
    private String _errorsStr = "";
    private boolean _triggerFlag = false;

    private static final Log _log = LogFactory.getLog(WebService.class.getName());
    private static final boolean _isDebugging = _log.isDebugEnabled();

    private Set<String> _inputNameSet = new HashSet<String>();
    private Set<String> _outputNameSet = new HashSet<String>();

} // end of WebService