|
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.
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.
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:
C:\axis1.1\
.C:\axis1.1\webapps\axis\
directory to the Tomcat deployment directory, such as C:\tomcat-4.1.31\webapps\
.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
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:
and custom 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.
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.
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}
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:
java.util.Map
object if there are multiple output parameters, or simply the value if there is only one output value.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: