Sunday, April 3, 2016

Extending WSIT using RabbitMQ and AMQP

Introduction

The Web Services Integration Toolkit (WSIT) for HP OpenVMS[1] is an extensible API-level integration technology developed by HP for the OpenVMS operating system that facilitates the integration of new or existing code written in 3GL languages with Java. Based on a supplied XML-based interface definition or by analyzing debug records in object code, tools provided by WSIT generate Java beans and C language wrapper code that can be used to construct an interface to the underlying API, which can then incorporated into essentially any type of Java application. In addition to this powerful code generation facility, the toolkit includes a runtime component that can be used to facilitate and manage IPC-based communication between Java code and the backend API in a client-server fashion (out of-process deployment). The runtime environment provides security features (via hooks into SYSUAF or ACMS), and supports basic tuning functionality to allow applications to be scaled as appropriate. Alternatively, wrapped API code can be called directly from Java code via JNI (in-process deployment).

The choice of deployment model and the configuration options for out-of-process deployment depend upon many factors, such as whether the wrapped code is thread-safe and re-entrant, whether the wrapped code maintains any sort of state, and so on; however WSIT includes functionality to accommodate all of these nuances. WSIT understands all OpenVMS data types and can be used with essentially any OpenVMS 3GL, generating code that uses the correct argument passing mechanisms for the language in question. It can also be used to generate interfaces for ACMS applications.

Figure 1. API-level integration. Existing 3GL business logic is exposed as a set of API calls. WSIT provides capabilities to generate code to facilitate calling these API functions from Java. Existing business logic is not disrupted, but can now be accessed via new interface mechanisms.
All of this sounds very appealing as a means of integrating modern Java-based application environments with what will often be business-critical legacy application code; however there is a catch. For inter-process communication with the out-of-process deployment model, WSIT uses the ICC (Intra Cluster Communication) protocol[2]. While the ICC protocol is very efficient, it is totally proprietary to the OpenVMS operating system. This means that one way or another it is necessary to run at least some Java component on the OpenVMS system in order to communicate with the wrapped code or ACMS application; it is not possible to take the generated Java code and use it on another (non-OpenVMS) platform to communicate with the OpenVMS application environment; nor is it even possible to use the generated Java code on another OpenVMS system that is not part of the cluster on which the wrapped application resides[3]. Whilst there is no particular issue with running Java on OpenVMS, this ICC-dependent model for all but the simplest off situations essentially solves only part of the integration problem (albeit an important and often problematical one), and in order for non-OpenVMS-based Java applications to interact with the wrapped OpenVMS-based application environment it will in addition be necessary to implement some form of integration between the generated Java beans that must reside on OpenVMS and the non-OpenVMS-based Java environment. Possibly this could be done in a platform-agnostic manner by using WSIT to generate a Web Services-based interface that could be used with Apache Tomcat and AXIS2 on OpenVMS; however API-level interfaces generally do not map well to business services (a business service will typically be comprised of some sequence of API operations), or such an approach might not be sufficiently performant, or it may place unacceptable additional load on the OpenVMS environment. Some users have developed solutions using tools such as OpenAdaptor (https://www.openadaptor.org/) with the WSIT-generated Java beans on OpenVMS to integrate with external systems, leveraging the various protocol adapters provided by these tools; however such solutions do not mitigate the potential problem of placing additional (and potentially significant) processing load (CPU and memory) on the existing OpenVMS environment.

As noted above, one of the key features of WSIT is its powerful code generation capabilities. This code generation is template-driven, using Apache Velocity (https://velocity.apache.org/) to generate code using templates written in the Velocity Template Language (VTL). Templates are provided for application wrapper generation, Java bean generation, and for the generation of the various sample Java interface types (POJO, JSP, and AXIS2) that use the generated Java beans.

What is not at all well elucidated in the WSIT documentation is that this powerful code generation facility can be extensively customized[4]: it is possible to modify the existing Velocity templates, and it is possible to introduce new templates. This extensibility opens up a raft of interesting possibilities. Most obviously, it is possible to modify existing templates (or create new templates) to generate code that includes additional or modified functionality, such as the inclusion of logging functions, use of alternative authentication mechanisms, and so on. However, perhaps not so obvious is the fact that it is possible to modify the templates (or create a new set of templates) to generate code that does not rely upon the OpenVMS-proprietary ICC-based WSIT runtime, but instead uses another protocol for inter-process communication. In other words, it is possible to completely bypass the ICC-based runtime and use an alternative non-proprietary cross-platform integration technology for inter-process communication between the wrapped application code and the generated Java beans while still fully leveraging the powerful code generation capabilities and knowledge of OpenVMS data types provided by the WSIT software, whereupon it becomes possible to use the generated Java code to communicate with the wrapped application from any platform that supports a suitable version of the JVM and the integration technology in question.

This post illustrates how the approach described above can be used to extend WSIT to use the Advanced Message Queuing Protocol (AMQP) with RabbitMQ in place of the ICC protocol to provide an efficient, scalable, and fault-tolerant integration solution. Implementation details are described and simple examples are provided to illustrate how the approach can be used to integrate with both 3GL code and ACMS applications. Note that the solution leverages work done previously[5] to develop a generic consumer for RabbitMQ that is able to dynamically load shareable images (shared libraries) containing functions that can be mapped to specific queues and routing keys.

Details

As commented above, WSIT provides two deployment mechanisms for wrapped applications, namely in-process deployment and out-of-process deployment. With the in-process model, the WSIT runtime dynamically loads the wrapped application (a shareable image), and all code therefore runs in the same address space. This model is obviously likely to be more efficient from a performance perspective as it negates any inter-processes communication; however it is not necessarily as scalable or fault-tolerant, and it is likely to be inappropriate if the legacy application being wrapped is not reentrant or thread-safe. Java is inherently multi-threaded and uses threading to achieve scalability. Many legacy OpenVMS applications are not reentrant or thread-safe, and in contrast scalability would often have been achieved by running multiple instances of the program in question (which is a processing model that works very well on OpenVMS). It would be possible to introduce a mutex to single-thread access to the shareable image; however this would likely introduce a major bottleneck. Accordingly, for any serious piece of work, the out-of-process model will invariably need to be used, and indeed for ACMS applications the out-of-process model must be used to avoid potentially catastrophic interactions between POSIX threads and ASTs (Asynchronous System Traps), which are heavily used by the ACMS SI API. For the out-of-process model, the WSIT runtime can be configured to handle non-reentrant wrapped applications by starting a separate instance of the application for each client, and for reentrant applications it can be configured to start some specified minimum number of processes and to scale dynamically by starting additional processes, up to some prescribed threshold, and culling processes (down to the prescribed minimum) when they are no longer required. If a processes crashes, the WSIT runtime will create a new instance, within prescribed limits (maximum number of restarts, delay between restarts, and so on). It should be noted that the approach described in this post to customize WSIT to use protocols other than ICC completely bypasses all of this runtime functionality, and if such functionality is required then it would need to be provided by the middleware for which code is being generated, or it would need to be developed. The purpose of this post is to illustrate how to bypass ICC whilst still leveraging the code generation capabilities of WSIT, and the text does not go into any particular detail with regard to replicating the runtime capabilities of WSIT for any particular middleware technology.

The following diagram illustrates the basic architecture for the WSIT out-of-process model. Based on a supplied interface definition (or by examining object file debug records) WSIT is able to generate a server wrapper and a set of Java beans that map to wrapped API functions. The generated server wrapper (C code) is linked with the legacy application (or ACMS SI API) to create an OpenVMS shareable image that can be called directly via JNI or called indirectly via the ICC-based runtime as described above. The Java beans can be incorporated into any Java application, and WSIT provides facilities for generating several types of sample application (POJO, JSP, AXIS2-based web service) that can serve as a starting point for further development and integration with external client applications. However, as the diagram illustrates, because the generated Java beans are dependent upon the OpenVMS-proprietary WSIT ICC-based runtime, they cannot be used directly by external non-OpenVMS clients (or from OpenVMS clients that are not part of the cluster hosting the wrapped application).

Figure 2. WSIT out-of-process deployment model. The wrapped application is linked as a shareable image that is dynamically loaded by the WSIT runtime. Various options are provided to tune the runtime environment in terms of scalability and fault-tolerance, and to cater for various characteristics of the wrapped application such as reentrancy and multi-threading. Generated Java beans communicate with the wrapped application via the WSIT runtime, and can be used to create OpenVMS-based Java components that provide integration points for use by external clients. Because the generated beans rely on the ICC-based runtime, they cannot be incorporated into and used from external client code.
To overcome this limitation of WSIT and bypass the use of ICC for inter-process communication whilst still leveraging the powerful code generation facilities provided by the product, the supplied Velocity-based code generation templates can be modified, or alternative sets of Velocity templates can be used. The following sections describe how WSIT can be configured to use different templates and illustrate an alternative set of templates that use AMQP instead of ICC for communication between generated Java beans and server wrapper code for out-of-process deployment.

Specifying alternative templates

Velocity-based code generation is performed using the Java application com.hp.wsi.Generator, which resides in the wsi$root:[lib]idl2code.jar archive. For example, for a given interface definition file (in this case math.xml), code generation might be performed as follows:

$ define/job java$classpath wsi$root:[lib]velocity-dep-1_4.jar, -
wsi$root:[tools]idl2code.jar
$ java "com.hp.wsi.Generator" -i math.xml -a math -o [.generated]

The java command instructs the code generation facility to process the math.xml interface definition and to create generated output in the [.generated] directory. The “-a option essentially specifies a name for the application, which must be unique within the set of WSIT applications deployed on a particular OpenVMS node or cluster. If the specified output directory does not exist, it will be created, and if it does already exist then new versions of generated files will be created. Note that in this particular case we did not instruct the code generation facility to generate sample code for one of the supported application types (POJO, JSP, AXIS2-based web service) types, so only the server wrapper and Java bean code is generated; this code will be created in the directories [.generated.mathServer] and [.generated.math], respectively.

By default, the WSIT code generation facility uses Velocity templates that reside in various sub-directories under wsi$root:[tools.templates], and in order to change the structure of the generated code the simplest approach would arguably be to just modify these existing templates; however such an approach is not particularly flexible, as different sets of templates might be required for different projects, and by different developers. Fortunately WSIT provides a better way to allow developers to specify different sets of templates: The code generation facility determines the search path for Velocity templates from the value of the property file.resource.loader.path specified in the velocity.properties file. By default the generator uses the velocity.properties file found in wsi$root:[tools.templates]; however via the “-p” command line option for com.hp.wsi.Generator it is possible to specify an alternative directory for the location of this properties file, thereby allowing developers to use their own properties files and to specify in those properties files the locations of any customized Velocity templates. For example, the following command instructs the generator to look for velocity.properties in /librabbitmq$root/templates/ (note the requirement to use UNIX syntax to specify the path):

$ java "com.hp.wsi.Generator" -i math.xml -a math -o [.generated] -
-p /librabbitmq$root/templates/

An important point to note is that while it is possible to specify alternative locations for template files, the names of the template files used the code generation facility are fixed – it is not possible to add new types of templates or template files with different names from those expected by the tool. This is not a particular restriction, as the set of template files provided by WSIT and the functionality they are intended to provide will be sufficient to meet most integration project requirements.

Using AMQP with RabbitMQ instead of ICC

As mentioned above, the solution described in this post leverages work done previously to develop a generic consumer application for RabbitMQ that is able to dynamically load OpenVMS shareable images containing entry points (functions) that can be mapped to specific queues and routing keys. A key point to note here is that this is analogous to the way that WSIT works: the C code wrapper layer generated by WSIT is compiled and linked into a shareable image that is dynamically loaded by the WSIT runtime, and messages processed by the WSIT runtime are mapped by the generated shareable image wrapper code to the relevant underlying API function. By designing an alternative protocol solution that operates in an analogous manner to the ICC-based WSIT runtime it is possible to significantly reduce the effort required to implement the new AMQP and RabbitMQ-based solution. Whilst for other protocols and middleware technologies it might be necessary to implement something similar to the generic RabbitMQ consumer application, most middleware technologies will be readily amenable to this type of approach, and the level of effort required to implement some sort of generic server layer should typically not be significant. 

As a consequence of this design approach, nearly all of the work required to implement the RabbitMQ-based solution involves changes to the Velocity template file (or the creation of a new template file) associated with the generation of the Java bean code, which must be modified to use RabbitMQ Java client API calls in place of WSIT API calls. Specifically, the interfaceimpl-java.vm template file needs to be modified (or a new file created) as follows:

  •  Import definitions for various RabbitMQ Java API client classes
  •  Define new (or different) private objects for the connection factory, connections, channels, and exchange details
  • Modify the constructor to accept and use additional parameters including username and password, exchange name, and communication timeout[6]
  • Modification of the remove() method to ensure that connections to RabbitMQ are closed cleanly upon normal client termination
  • Modification of the loop that generates code for each method call to use the RabbitMQ Java API RpcClient() class instead of using mShell.invoke() to call into the WSI$JNISHR.EXE shareable image to communicate with the server process via ICC

A complete copy of the modified template file is included below in Appendix 1.

As can be seen, the changes are relatively minimal, and it is likely that changes to the templates would be similarly straightforward for other middleware technologies. Note that changes to other template files are not required for the RabbitMQ-based solution; however such changes may be necessary with other middleware technologies.

General observations and limitations

The solution has been tested using Java clients running on Windows and Linux and the RabbitMQ broker running on Linux and OpenVMS. Transaction rates of several hundred synchronous remote procedure calls (RPC’s) per second easily achieved for single-threaded clients using these configurations, and it is likely that considerable higher overall rates can be achieved by using asynchronous RPC's, where the client does not block waiting for each response. The solution can leverage clustered RabbitMQ brokers as illustrated below (for the case of an ACMS-based application) to provide excellent scalability and fault-tolerance.

Figure 3. Clustered RabbitMQ brokers can be used to enhance scalability and fault tolerance. Note that changes to the code generation template for the Java client will be required in order to take full advantage of the clustered configuration. Optionally, some form of load balancer (such as HAProxy) may be placed between the clients and the RabbitMQ cluster to provide connection balancing and to simplify client re-connection handling.
The solution is presently restricted to use with Java-based clients or other languages that use the Java virtual machine; however it would be possible with some effort to implement .NET-based equivalents of the relevant WSIT Java classes (primarily those classes that understand OpenVMS data types), whereupon it would be possible to use the RabbitMQ .NET client and implement Velocity templates for generation of the required C# code. Similarly it would be possible to support other popular languages, including scripting languages such as Ruby and Python.

Other enhancements might include: 
  • Authenticating and authorizing users by hooking into SYSUAF or via LDAP instead of using RabbitMQ’s default authentication and authorization mechanism
  • Modification of the generic AMQP server component to dynamically scale based on load (within given constraints), support restarts for failed processes, and so on (it is presently necessary to pre-start some static number of server processes)

Summary

This post describes how the template-driven WSIT code generation facilities can be customized to extend WSIT to use the Advanced Message Queuing Protocol (AMQP) with RabbitMQ in place of the OpenVMS-based ICC protocol to provide an efficient, secure, scalable, fault-tolerant, and fully cross-platform integration solution. The solution may be readily adapted to use with other Open Standard integration protocols such as Gearman, ZeroMQ, or STOMP, and the solution can potentially be extended to generate client code for languages other than Java. From a development perspective, generated client code is straightforward to incorporate into existing Java applications, and WSIT’s knowledge of OpenVMS datatypes and programming languages (and ACMS) makes it possible to wrap legacy application code with relative ease. The net result is a scalable and robust integration solution that permits legacy OpenVMS-based applications to interoperate and exchange data with external systems using Open Standards-based integration technologies, allowing OpenVMS users to both preserve and enhance their investment in the OpenVMS platform.




[2] To be somewhat more precise, ICC is the only protocol that can currently be used by the WSIT runtime. WSIT evolved from another HP integration technology for OpenVMS called BridgeWorks, which supported a number of protocols, including ICC, DCE, and COM. BridgeWorks provided a rich cross-platform integration solution that could be used to integrate both Java-based and Windows-based client applications with OpenVMS-based application environments. Much of the WSIT runtime was taken directly from BridgeWorks; however only the ICC protocol was left enabled. It would in theory be possible to enable the other protocols; however a better approach might be to incorporate into the runtime support for more modern and open standards-based protocols such as AMQP, ZeroMQ, STOMP, WebSockets, and so on.
[3] The fact that WSIT uses ICC for inter-process communication means that within an OpenVMS cluster it is possible to run the wrapped application and the generated Java code on different cluster nodes, thereby distributing the processing load and potentially enhancing availability. The fact that these components can be run on separate cluster nodes is not well-documented.
[4] With the latest versions of WSIT (V3.4-1) there an example is provided that illustrates how to add basic custom extensions to the code generation process; however this my no means illustrates the full extent of the customization that is possible.
[6] It may be desirable to further modify the constructor (or to provide additional methods) to allow specification of heartbeat frequency and a list of broker addresses to facilitate use in a clustered broker environment.



Appendix 1 – modified interfaceimpl-java.vm template file

#set( $wsibuffer = 'wsi$buffer' )
#set( $wsiacontext = 'wsi$acontext' )
#set( $wsioutbuffer = 'wsi$outbuffer' )
#set( $server = $application.Server )

package ${application.Name};

// Core java packages
import java.util.StringTokenizer;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;

// Web Services based Holder classes
import javax.xml.rpc.holders.ByteHolder;
import javax.xml.rpc.holders.DoubleHolder;
import javax.xml.rpc.holders.BigDecimalHolder;
import javax.xml.rpc.holders.BigIntegerHolder;
import javax.xml.rpc.holders.CalendarHolder;
import javax.xml.rpc.holders.FloatHolder;
import javax.xml.rpc.holders.IntHolder;
import javax.xml.rpc.holders.LongHolder;
import javax.xml.rpc.holders.ObjectHolder;
import javax.xml.rpc.holders.ShortHolder;
import javax.xml.rpc.holders.StringHolder;

// WSI based classes
import com.hp.wsi.ServerConfig;
import com.hp.wsi.StringQualify;
import com.hp.wsi.WsiBuffer;
import com.hp.wsi.WsiDecimal;
import com.hp.wsi.WsiArray;
import com.hp.wsi.WsiStructure;
import com.hp.wsi.WsiV2Structure;
import com.hp.wsi.WsiException;
import com.hp.wsi.WsiIpcContext;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.RpcClient;

/**
 *  Implementation class for the I${interface.Name} interface.
 */
public class ${interface.Name}Impl implements I${interface.Name} {

    private WsiBuffer wsi$buffer        = new WsiBuffer(512);
    private WsiBuffer wsi$outbuffer     = new WsiBuffer();
    private byte []   wsi$retbuf;

    private int wsi$timeout = -1;
    private String wsi$exchange = "amq.direct";

    private ConnectionFactory cfconn;
    private Connection conn;
    private Channel ch;

#foreach( $routine in $server.Routines)
    private RpcClient wsi$svc_${routine.Methodid} = null;
#end
#foreach( $routine in $server.Acmss)
    private RpcClient wsi$svc_${routine.Methodid} = null;
#end


    /**
     * ${interface.Name}Impl
     *      intstantiates a default ${interface.Name}Impl object.
     *      This constructor is used to setup an in-process environment, where
     *      both the javabean and the server wrapper are in the same process space.
     *
     * @throws com.hp.wsi.WsiException
     *
     */
    public ${interface.Name}Impl(String host, int port, String username, String password, String exchange, int timeout) throws WsiEx
ception
    {
        try {
            cfconn = new ConnectionFactory();
            cfconn.setHost(host);
            cfconn.setPort(port);
            cfconn.setUsername(username);
            cfconn.setPassword(password);
            conn = cfconn.newConnection();
            ch = conn.createChannel();
            wsi$timeout = timeout;
            wsi$exchange = exchange;
        }
        catch (Exception e) {
            System.err.println("Main thread caught exception: " + e);
            e.printStackTrace();
            System.exit(1);
        }
    } // default constructor


    public ${interface.Name}Impl(String uri, String exchange, int timeout)
    {
        try {
            cfconn = new ConnectionFactory();
            cfconn.setUri(uri);
            conn = cfconn.newConnection();
            ch = conn.createChannel();
            wsi$timeout = timeout;
            wsi$exchange = exchange;
        }
        catch (Exception e) {
            System.err.println("Main thread caught exception: " + e);
            e.printStackTrace();
            System.exit(1);
        }
    }


    /**
     * remove()
     *    Cleans up all session context information being
     *    maintained for this JavaBean.   If an IPC is being
     *    used for inter-process communication, this channel
     *    will be closed in the process.   (Note that it is
     *    cleaner to explicitly call remove() in order to
     *    close the session.)
     *
     * @throws com.hp.wsi.WsiException
     *
     */
    public void remove () throws WsiException {
            try {
               if ( null != conn ) {
                   conn.close();
                   conn = null;
               }
            }
            catch( Exception e ) {
               throw new WsiException("WSI - Problem closing connection", e );
            }

    } // remove


#foreach( $method in $interface.Methods )
#set( $routine = $method.LinkedItem )

    /**
     *
     * ${method.Name}
     *      ${routine.Description}
     *
#foreach( $param in $routine.Parameters)
     * @param $param.Name   parameter has type ${param.JBFormalDefinition}( ${param.Typedef} )
#end
#if( $routine.Returnparam )
     * @return              returns type ${routine.Returnparam.JBFormalDefinition}( ${routine.Returnparam.Typedef} )
#end
     *
     * @throws com.hp.wsi.WsiException
     *
     */
#set( $paramformal = "")
#foreach( $param in $routine.Parameters)
#if( $velocityCount == 1 )
#set( $paramformal = "$param.JBFormalDefinition $param.Name" )
#else
#set( $paramformal = "$paramformal, $param.JBFormalDefinition $param.Name" )
#end
#end
    public#if($routine.Returnparam) $dtutility.getJBtype($routine.Returnparam.Datatype)#else void#end ${routine.WebServiceName} ($pa
ramformal)
                    throws WsiException
    {

#if( $routine.Returnparam )
       $dtutility.getJBtype($routine.Returnparam.Datatype) wsi$retval;
#end
       int wsi$valuesize;
       try
       {
           wsi$buffer.resetPosition();
#if( $routine.Argcount > 0 )
           ${wsibuffer}.putParamHeader($routine.Argcount);
#end

#foreach( $param in $routine.Parameters)
#if( $param.Arrayinfo )
#if( $param.Passingmechanism < 3 )
#set( $byref = "true" )
#else
#set( $byref = "false" )
#end
#if( $param.Nullterminated )
#set( $nullterm = 1 )
#else
#set( $nullterm = 0 )
#end
           ${wsibuffer}.putParamEntry( $param.Datatype, $param.ClassType,#if( $param.DynStringParam ) 0#else $param.Size#end, $param
.InverseScale );
           WsiArray wsi$${param.Name} = new WsiArray(${param.Name}#if( $param.Usage > 1 ).value#end, wsi$buffer, $param.Datatype,
                                    $param.ClassType, $param.Size, $param.InverseScale, $param.Arrayinfo.Rowbycolumn, $nullterm, "#i
f( $param.Passingmechanism < 3 )${param.Arrayinfo.DimString}#else${param.Arrayinfo.Dimcount}#end", $byref, true);
           wsi$${param.Name}.exportArray(wsi$buffer);
#else
           ${wsibuffer}.putParamEntry( $param.Datatype, $param.ClassType,#if( $param.Size == 0 ) ${param.Name}#if( $param.Usage > 1
).value#end.length()#else $param.Size#end, $param.InverseScale );
           ${wsibuffer}.put(#if( $param.Structure )(WsiV2Structure)#end${param.Name}#if( $param.Usage > 1 ).value#end, ${param.Datat
ype}${param.OptionalPutArgs});
#end
#end
           if (wsi$svc_${routine.Methodid} == null) {
              wsi$svc_${routine.Methodid} = new RpcClient(ch, wsi$exchange, "${method.Name}", wsi$timeout);
           }
           ${wsioutbuffer}.setBuffer(wsi$svc_${routine.Methodid}.primitiveCall(${wsibuffer}.getBuffer()));

#if( $routine.HasOutputBuffer )
           wsi$outbuffer.getParamHeader(0);
#end
#if( $routine.Returnparam )
           wsi$valuesize = ${wsioutbuffer}.getParamEntry();
           wsi$retval = ${wsioutbuffer}.get${routine.Returnparam.JBRetType}( $routine.Returnparam.Datatype );
#end

#foreach( $param in $routine.Parameters)
#if( $param.Usage > 1 )
           wsi$valuesize = wsi$outbuffer.getParamEntry();
#if( $param.Arrayinfo )
#if( $param.ResizableArray )
           wsi$${param.Name}.importNewByteArray(wsi$outbuffer, wsi$valuesize);
#else
           wsi$${param.Name}.importArray(wsi$outbuffer);
#end
           ${param.Name}.value = wsi$${param.Name}.getValue();
#else
#if( $param.Structure )
           ${wsioutbuffer}.getStructure((WsiV2Structure)${param.Name}.value);
#else
           ${param.Name}.value = ${wsioutbuffer}.get${param.JBRetType}( ${param.Datatype}${param.OptionalGetArgs});
#end
#end
#end
#end
       }
       catch (WsiException we)
       {
           throw we;
       }
       catch (Exception ex)
       {
           throw new WsiException(ex);
       }

       return#if( $routine.Returnparam ) wsi$retval#end;

    } // ${method.Name}
#end


#foreach( $method in $interface.AcmsMethods )
#set( $routine = $method.LinkedItem )
    /**
     *
     * ${method.Name}
     *      ${routine.Description}
     *
#foreach( $param in $routine.Parameters)
     * @param $param.Name    parameter has type ${param.JBFormalDefinition}
#end
     *
     * @throws com.hp.wsi.WsiException
     *
     */
#set( $paramformal = "")
#foreach( $param in $routine.Parameters)
#if( $velocityCount == 1 )
#set( $paramformal = "$param.JBFormalDefinition $param.Name" )
#else
#set( $paramformal = "$paramformal, $param.JBFormalDefinition $param.Name" )
#end
#end
    public void ${routine.WebServiceName} ($paramformal)
                    throws WsiException
    {

       int wsi$valuesize;
       try
       {
           ${wsibuffer}.resetPosition();
           ${wsibuffer}.putParamHeader(${routine.Argcount}+1);

           ${wsibuffer}.putParamEntry(WsiBuffer.DTYPE_T, WsiBuffer.CLASS_S, 0, 0);

#foreach( $param in $routine.Parameters)
           ${wsibuffer}.putParamEntry( $param.Datatype, $param.ClassType,#if( $param.Size == 0 ) ${param.Name}#if( $param.Usage > 1
).value#end.length()#else $param.Size#end, $param.InverseScale );
#if( $param.Arrayinfo )
#if( $param.Usage > 1 )
#set( $byref = "true" )
#else
#set( $byref = "false" )
#end
#if( $param.Nullterminated )
#set( $nullterm = 1 )
#else
#set( $nullterm = 0 )
#end
           WsiArray wsi$${param.Name} = new WsiArray(${param.Name}#if( $param.Usage > 1 ).value#end, wsi$buffer, $param.Datatype,
                                    $param.ClassType, $param.Size, $param.Scale, $param.Arrayinfo.Rowbycolumn, $nullterm, "#if( $par
am.Passingmechanism < 3 )${param.Arrayinfo.DimString}#else${param.Arrayinfo.Dimcount}#end", $byref, true);
           wsi$${param.Name}.exportArray(wsi$buffer);
#else
           ${wsibuffer}.put(#if( $param.Structure )(WsiV2Structure)#end${param.Name}#if( $param.Usage > 1 ).value#end, ${param.Datat
ype}${param.OptionalPutArgs});
#end
#end
           if (wsi$svc_${routine.Methodid} == null) {
              wsi$svc_${routine.Methodid} = new RpcClient(ch, wsi$exchange, "${method.Name}", wsi$timeout);
           }
           ${wsioutbuffer}.setBuffer(wsi$svc_${routine.Methodid}.primitiveCall(${wsibuffer}.getBuffer()));

           ${wsioutbuffer}.getParamHeader(0);

           wsi$valuesize = wsi$outbuffer.getParamEntry();
           String wsi$extstatus = ${wsioutbuffer}.getString(WsiBuffer.DTYPE_T,wsi$valuesize);

#foreach( $param in $routine.Parameters)
#if( $param.Usage > 1 )
           wsi$valuesize = wsi$outbuffer.getParamEntry();
#if( $param.Arrayinfo )
#if( $param.ResizableArray )
           wsi$${param.Name}.importNewByteArray(wsi$outbuffer, wsi$valuesize);
#else
           wsi$${param.Name}.importArray(wsi$outbuffer);
#end
           ${param.Name}.value = wsi$${param.Name}.getValue();
#else
#if( $param.Structure )
           ${wsioutbuffer}.getStructure((WsiV2Structure)${param.Name}.value);
#else
           ${param.Name}.value = ${wsioutbuffer}.get${param.JBRetType}( ${param.Datatype}${param.OptionalGetArgs});
#end
#end
#end
#end
       }
       catch (WsiException we)
       {
           throw we;
       }
       catch (Exception ex)
       {
           throw new WsiException(ex);
       }

       return;

    } // ${method.Name}
#end
} // class