JudoScript.COM Design principles of Judo the sport and the language
HomeJudo LanguageJuSP PlatformJamaica Language 
Judo ReferenceJuSP ReferenceWiki/WeblogTutorials/PresentationsDownloadsGoodiesFeedback  
Book: The Judo Language 0.9
 








In this chapter:

Chapter 28. SOAP Web Service Scripting

By James Jianbo Huang

printer-friendly version
Synopsis: Judo supports SOAP web service scripting in a very easy way. Basically, you obtain a WSDL handle, and treat the WSDL handle as a regular object by invoking its operations like calling a method on an object. Various data types can be passed to the operations, and the output parameters are returned in a map if multiple output parameters are returned; if there is only one output parameter, its value is returned instead. This chapter introduces to the basics of SOAP web services, detailed on the issues regarding web service scripting in Judo, and finishes with a case study that presents a program that utilizes the Google search API.

 
Introduction to SOAP Web Services   to be done

SOAP Web Services


WSDL


Data Types and Encoding


Web Service Clients



 
SOAP Web Service Scripting

Prerequisite

In order to script web services in Judo, you need to download the Apache Axis version 1.1 or up software and put all the Java executable library jar files in the classpath. As a convenience, here is a single jar file for all the necessary libraries that you can download; it is an agglomeration of all the jar files in the Axis 1.1 distribution.


Start Scripting Web Services

In Judo, scripting SOAP web services starts with obtaining the WSDL handle via the wsdl:: operator, and simply call the operations of the service in the WSDL file. Here is an example:

Listing 28.1 wsdltest.judo
svc = wsdl::'http://coldrooster.com/dhtmldude/mathservice.asmx?WSDL';

println '3 + 2 = ', svc.Add(3, 2);
println '3 - 2 = ', svc.Subtract(3, 2);
println '3 * 2 = ', svc.Multiply(3, 2);
println '3 / 2 = ', svc.Divide(3, 2);

The WSDL file for this service has a <service> element like this:

<definitions>
  ......
  <service name="MathService">
    <port name="MathServiceSoap" binding="s0:MathServiceSoap">
      <soap:address location="http://coldrooster.com/dhtmldude/mathservice.asmx" /> 
    </port>
    <port name="MathServiceHttpGet" binding="s0:MathServiceHttpGet">
      <http:address location="http://coldrooster.com/dhtmldude/mathservice.asmx" /> 
    </port>
    <port name="MathServiceHttpPost" binding="s0:MathServiceHttpPost">
      <http:address location="http://coldrooster.com/dhtmldude/mathservice.asmx" /> 
    </port>
  </service>
</definitions>

Each port has a binding of the four arithmetic operations: Add, Subtract, Multiply and Divide. Each operation, in turn, is associated with their paremeters; in this case, they all take two float numbers as input and return a float as result.

Notice that in the above example, we only obtained the WSDL document and then call the operations directly without references to the service name nor the port name. By default, Judo looks for the first serrvice element in the WSDL. You can explicitly specify the service name in the wsdl:: operator; its complete syntax is:

WSDL ::= wsdl:: WSDL_URL [ : ServiceName ]
WSDL_URL ::= Expr
ServiceName ::= Expr

where ServiceName is an expression that evaluates to a QName (qualified name), which can be a javax.xml.naming.QName instance, or a string of the form {namespace}local_part. The following program does exactly the same as the previous example:

wsdlURL = 'http://coldrooster.com/dhtmldude/mathservice.asmx?WSDL';
svcName = '{http://tempuri.org/}MathService';
svc = wsdl::wsdlURL : svcName;
......

Also, within the service element in the WSDL, there are multiple ports. By default, Judo looks for the port with SOAP binding. If you wish to, you can explicitly specify the port names:

wsdlURL = 'http://coldrooster.com/dhtmldude/mathservice.asmx?WSDL';
svcName = '{http://tempuri.org/}MathService';
svc = wsdl::wsdlURL : svcName;

println '3 + 2 = ', svc.MathServiceSoap.Add(3, 2);
println '3 - 2 = ', svc.MathServiceSoap.Subtract(3, 2);
println '3 * 2 = ', svc.MathServiceSoap.Multiply(3, 2);
println '3 / 2 = ', svc.MathServiceSoap.Divide(3, 2);

To summarize, scripting SOAP web services in Judo is quite similar to scripting any value systems such as Java scripting and COM/ActiveX scripting. You obtain a handle to a WSDL document, then simply invoke its operations just like calling methods on an object. As we mentioned earlier, web services can take both input and output parameters, and return value and parameters are typed. In the next section, we will detail on parameters and data types.



 
Parameters and Return Values

Web services are meant for remote procedure calls accross heteorogeneous systems. We use an inter-operation sample web service, echo, from the Apache Axis software to demonstrate using various types of data for parameters and return values.

Set Up the Echo Sample Web Service

All the programs in this section invoke the echo web service, run on the local machine. To start this service, you need to deploy the Axis webapp to a servlet container (such as Tomcat), and register the echo web service with that Axis webapp. The follow are the exact steps to set it up:

  1. Start a Tomcat server, say, on port 8080.
  2. Download the Apache Axis version 1.1 or 1.2 software and unpack to a directory such as C:\axis1.1\.
  3. Deploy the Axis webapp by copying the C:\axis1.1\webapps\axis\ directory to the Tomcat deployment directory, such as C:\tomcat-4.1.31\webapps\.
  4. Move to C:\axis1.1\samples\echo\ directory, (assuming you have added all the jar files in C:\axis1.1\lib\ to your classpath,) and run:
    java org.apache.axis.client.AdminClient deploy.wsdd
    
  5. Open the web browser and enter this URL: http://localhost:8080/axis/index.html. Click on the link of "View the list of deployed Web services", and you should see an echo service, whose WSDL document is located at http://localhost:8080/axis/services/echo?wsdl. This is the WSDL we will use throughout this section.

The echo service includes operations that take a parameter and returns it. Sometimes the values returned are of different types. Here is a listing of all its services, grouped in two categories: built-in types:

  • echoString
  • echoNormalizedString
  • echoToken
  • echoInteger
  • echoFloat
  • echoUnsignedLong
  • echoUnsignedInt
  • echoUnsignedShort
  • echoUnsignedByte
  • echoNonNegativeInteger
  • echoPositiveInteger
  • echoNonPositiveInteger
  • echoNegativeInteger
  • echoBase64
  • echoHexBinary
  • echoDecimal
  • echoBoolean
  • echoDate
  • echoMap
  • echoVoid

and custom types:

  • echoStringArray
  • echoIntegerArray
  • echoFloatArray
  • echoStruct
  • echoStructArray
  • echoStructAsSimpleTypes
  • echoSimpleTypesAsStruct
  • echo2DStringArray
  • echoNestedStruct
  • echoNestedArray
  • echoMapArray

Built-In Data Types

The built-in types in SOAP correspond to primitive Judo data types, byte arrays, date/time and Object.

Listing 28.2 builtin_types.judo
wsdl = wsdl::'http://localhost:8080/axis/services/echo?wsdl';

byteArray = new byte[]{ 1, 2, 3 };
map       = { a=1, b=2, c=3 };

println 'echoString:             ', wsdl.echoString( 'string' );
println 'echoNormalizedString:   ', wsdl.echoNormalizedString( 'normalized string' );
println 'echoToken:              ', wsdl.echoToken( 'token a' );
println 'echoInteger:            ', wsdl.echoInteger( 9 );
println 'echoFloat:              ', wsdl.echoFloat( 9 );
println 'echoUnsignedLong:       ', wsdl.echoUnsignedLong( 2 );
println 'echoUnsignedInt:        ', wsdl.echoUnsignedInt( 2 );
println 'echoUnsignedShort:      ', wsdl.echoUnsignedShort( 2 );
println 'echoUnsignedByte:       ', wsdl.echoUnsignedByte( 2 );
println 'echoNonNegativeInteger: ', wsdl.echoNonNegativeInteger( 2 );
println 'echoPositiveInteger:    ', wsdl.echoPositiveInteger( 2 );
println 'echoNonPositiveInteger: ', wsdl.echoNonPositiveInteger( -2 );
println 'echoNegativeInteger:    ', wsdl.echoNegativeInteger( -2 );
println 'echoDecimal:            ', wsdl.echoDecimal( 123 );
println 'echoBoolean:            ', wsdl.echoBoolean( true );
println 'echoDate:               ', wsdl.echoDate( Date(2004,11,19) ).getTime();
println 'echoBase64:             ', wsdl.echoBase64( byteArray );
println 'echoHexBinary:          ', wsdl.echoHexBinary( byteArray );
println 'echoMap:                ', wsdl.echoMap( map );
println 'echoVoid().',              wsdl.echoVoid();

The result is:

echoString:             string
echoNormalizedString:   normalized string
echoToken:              token a
echoInteger:            9
echoFloat:              9.0
echoUnsignedLong:       2
echoUnsignedInt:        2
echoUnsignedShort:      2
echoUnsignedByte:       2
echoNonNegativeInteger: 2
echoPositiveInteger:    2
echoNonPositiveInteger: -2
echoNegativeInteger:    -2
echoDecimal:            123
echoBoolean:            true
echoDate:               11/19/04 12:00 AM
echoBase64:             [1,2,3]
echoHexBinary:          [1,2,3]
echoMap:                {a=1, c=3, b=2}
echoVoid().

You should compare the results to the WSDL of http://localhost:8080/axis/services/echo?wsdl, examine each operation's request and response. For instance, the operation echoBase64 has such definitions:

<definition>
  ......
  <wsdl:message name="echoBase64Response">
    <wsdl:part name="return" type="xsd:base64Binary" /> 
  </wsdl:message>
  <wsdl:message name="echoBase64Request">
    <wsdl:part name="inputBase64" type="xsd:base64Binary" /> 
  </wsdl:message>
  ......
  <wsdl:operation name="echoBase64" parameterOrder="inputBase64">
    <wsdl:input message="impl:echoBase64Request" name="echoBase64Request" /> 
    <wsdl:output message="impl:echoBase64Response" name="echoBase64Response" /> 
  </wsdl:operation>
  ......
</definition>

The return values are converted back to Judo data types; some of them are actually Java data types such as byte arrays and java.util.Map instances. The echoDate operation takes and returns a xsd:datetime value, which is converted to a java.util.Calendar.

Custom Data Types

Web services can define new data types by defining arrays and structs, whose elements themselves can be built-in types and other custom types. The following program tests a permutation of these cases:

Listing 28.3 custom_types.judo
wsdl = wsdl::'http://localhost:8080/axis/services/echo?wsdl';

string2d    = [ [ 'a', 'b' ], [ 'A', 'B' ] ];
mapArray    = [ new Object( alfa=1 ), new Object( beta='xyz' ) ];
soapStruct  = { varString='xyz', varInt=1, varFloat=1.01 };
structArray = [ soapStruct, soapStruct ];
soapStruct1 = { varString='xyz', varInt=1, varFloat=1.01, varStruct=soapStruct };
soapStruct2 = { varString='xyz', varInt=1, varFloat=1.01, varArray=[ 'x', 'y' ]};

println 'echoStringArray:         ', wsdl.echoStringArray( [ 'o', 'p', 'q' ] );
println 'echoIntegerArray:        ', wsdl.echoIntegerArray( [ 10, 20, 30 ] );
println 'echoFloatArray:          ', wsdl.echoFloatArray( [ 1.01, 2.02, 3.03 ] );
println 'echo2DStringArray:       ', wsdl.echo2DStringArray( string2d );
println 'echoMapArray:            ', wsdl.echoMapArray( mapArray );
println 'echoStruct:              ', wsdl.echoStruct( soapStruct );
println 'echoStructArray:         ', wsdl.echoStructArray( structArray );
println 'echoNestedStruct:        ', wsdl.echoNestedStruct( soapStruct1 );
println 'echoNestedArray:         ', wsdl.echoNestedArray( soapStruct2 );
println 'echoSimpleTypesAsStruct: ', wsdl.echoSimpleTypesAsStruct( 'xyz', 1, 1.01 );
println 'echoStructAsSimpleTypes: ', wsdl.echoStructAsSimpleTypes( soapStruct );

Judo maps values of various data types to the parameters specified in the WSDL. The soapStruct is an Object of three fields: varString, varInt and varFloat. The structArray is an array of a few soapStruct instances. The soapStruct1 has one more field, varStruct, that points to soapStruct. Finally, soapStruct2 takes a varArray that points to a string array.


Input/Output Parameters and Return Values

Operations defined in WSDL can have multiple input, output and input-output parameters. Since parameters are named, when there are multiple output (including input-output) parameters, a map is returned. In the above example, the last call, echoStructAsSimpleTypes, takes a struct but returns three values: outputString, outputInteger and outputFloat. Its definition is like this:

<definition>
  ......
  <wsdl:message name="echoStructAsSimpleTypesRequest">
    <wsdl:part name="inputStruct" type="tns1:SOAPStruct" /> 
  </wsdl:message>
  ......
  <wsdl:message name="echoStructAsSimpleTypesResponse">
    <wsdl:part name="outputString" type="xsd:string" /> 
    <wsdl:part name="outputInteger" type="xsd:int" /> 
    <wsdl:part name="outputFloat" type="xsd:float" /> 
  </wsdl:message>
  ......
  <wsdl:operation name="echoStructAsSimpleTypes"
    parameterOrder="inputStruct outputString outputInteger outputFloat">
    <wsdl:input message="impl:echoStructAsSimpleTypesRequest" name="echoStructAsSimpleTypesRequest" /> 
    <wsdl:output message="impl:echoStructAsSimpleTypesResponse" name="echoStructAsSimpleTypesResponse" /> 
  </wsdl:operation>
  ......
</definition>

The output of the echoStructAsSimpleTypes is:

echoStructAsSimpleTypes: {outputFloat=1.01, outputInteger=1, outputString=xyz}

If there is only one output parameter, the value is returned without its name.

As for the input and input-output parameters, since Judo doesn't support named parameters (yet?), they have to be specified in the order as defined in the WSDL.


Different Input Types for Maps and Structs

The difference between SOAP map and struct parameters is that, structs have predefined fields, whereas maps are collections of arbitrary name-value pairs. In Judo, for SOAP structs, you can pass any data structures and objects (including Java objects, COM/ActiveX components, etc.) that have the named properties; for SOAP maps, you can pass Object and user-defined classes as well as java.util.Map instances as SOAP map parameters. The following example demonstrates this:

Listing 28.4 map_values.judo
wsdl = wsdl::'http://localhost:8080/axis/services/echo?wsdl';

judoObj = { varString='xyz', varInt=1, varFloat=1.01 };
javaMap = new java::HashMap( varString='xyz', varInt=1, varFloat=1.01 );
javaObj = new java::Object {
            String varString;
            int    varInt;
            float  varFloat;

            constructor {
              super();
              varString = 'xyz';
              varInt = 1;
              varFloat = 1.01;
            }
          };

println 'Object      as Map:        ', wsdl.echoMap( judoObj );
println 'Java Map    as Map:        ', wsdl.echoMap( javaMap );

println 'Object      as SOAPStruct: ', wsdl.echoStruct( judoObj );
println 'Java Map    as SOAPStruct: ', wsdl.echoStruct( javaMap );
println 'Java Object as SOAPStruct: ', wsdl.echoStruct( javaObj );

First of all, we established values of three different types with the same field values: a Judo Object, a java.util.HashMap and a Java object created as a Java adapter. The Object and HashMap are passed to the echoMap and echoStruct operations, and the Java object was passed to echoStruct only. The result is:

Object      as Map:        {varInt=1, varString=xyz, varFloat=1.01}
Java Map    as Map:        {varInt=1, varString=xyz, varFloat=1.01}
Object      as SOAPStruct: {varInt=1,varString=xyz,varFloat=1.0099999904632568}
Java Map    as SOAPStruct: {varInt=1,varString=xyz,varFloat=1.0099999904632568}
Java Object as SOAPStruct: {varInt=1,varString=xyz,varFloat=1.0099999904632568}

Summary of SOAP Web Service Scripting

To summarize, scripting SOAP web services in Judo is very similar to scripting other value systems such as Java scripting and COM/ActiveX scripting. The rules are simple and natural:

  1. You obtain a handle to a WSDL documentation.
  2. Call its operations just like invoking methods on an object. Judo marshals the values to the intended parameter data types defined in the WSDL.
  3. Invocation input parameters are passed in to the operation in the order as specified in the WSDL.
  4. Return values are returned in a java.util.Map object if there are multiple output parameters, or simply the value if there is only one output value.
  5. For SOAP maps and structs, various types of data structures can be passed in as long as they hold name-value pairs. For SOAP structs, the defined fields must be present in those data structures; custom Java class instances (or Java beans) can also be passed in, which must define properties corresponding to the SOAP struct definition.

With this and the plethora of available web services, including those created by yourselves, you can truly enjoy the next-generation of webified information technologies! We conclude Judo web service scripting with a case study that takes advantage of the Google search web service client.

Google ... TODO:



 
back to top
 



Copyright © 2001-2005 JudoScript.COM. All Rights Reserved.