No bundle comment found. Component comments follow
PackageDescription: Opentalk-HTTP
This is HTTP transport infrastructure. HTTP transport can be used with either the simple XML marshaler or the SOAP marshaler.
Two versions of HTTP Transport are implemented. HTTPClientTransport just wraps Net.HttpClient to do the actual HTTP work, therefore it is only usable for request clients. HTTPTransport implements both an HTTP client and an HTTP server, however it is far from being a fully featured HTTP server, so there might be features that are important in some circumstances. For example there's no built in support for firewall proxies yet, that's why the HTTPClientTransport will still be useful in case you have to use a proxy to get to an external SOAP server. For setting up a server you may want to consider using the CGITransport from the Opentalk-CGI package.
Note that HTTPTransport assumes that application messages can carry contextual information (both XML and SOAP messages do). The transport compiles an "environment" dictionary from all the header fields of the transport message and attempts to install it into the message during unmarshaling. This allows the application to access the header fields if necessary.
Configuration message for HTTPClientTransport is #chttp. The transport is implemented as a datagram transport therefore it has to be used with a connection-less adaptor. Here is an example of how to setup a SOAP broker for it:
To configure and HTTPTransport use message #http. The transport is a StreamTransport therefore it has to be used with a connection-oriented transport. Here is an example of how to setup a SOAP broker for it:
This is an extension of Opentalk-HTTP to support web-server mediated communication through VisualWave CGI relays.
CGITransport simply sits on a socket and waits for requests from the relay. CGITransport is obviously only usable for servers, not clients.
Note that CGITransport assumes that application messages can carry contextual information (both XML and SOAP messages do).The transport compiles an "environment" dictionary from the environment variables passed by the CGI relay and attempts to install it into the message during unmarshaling. This allows the server application to access the header fields if necessary (e.g. when trying to hook into the security features of the webserver).
Corresponding configuration message for TransportConfiguration is #cgi. The transport is a StreamTransport therefore it has to be used with a connection-oriented transport. Here is an example setting up a SOAP broker using CGI:
This package integrates the generic XMLObject marshaling framework (package XMLObjectMarshalers) with Opentalk allowing to use an XML based application protocol for remote communication. The protocol is defined by an XMLObjectBinding. All entities carried by the messages, i.e. objects, exceptions, message formats, have to be defined in the binding. The marshaler is initialized with an instance of XMLObjectBinding wrapped in an instance of XMLOperationMap mapping smalltalk selectors to corresponding request/reply entity types. The XML protocol has to be used in combination with a lower level transport protocol, like HTTP, which has to be also loaded in order to set up an XML broker.
The XML marshaler configuration method is #xml. An XML marshaler configuration has a required parameter #binding: which takes an instance of the XMLOperationMap as the parameter. An instance of XMLOperationMap is created on an instance of binding using the #binding: instance creation method. The selector mapping has to be built using #add:request:reply: which takes the selector, request struct tag name and reply struct tag name as the parameters.
Let's build a simple random number generator service as an example. It will support a single request NextBatch that will return a sequence of random numbers. For the service implementation we will simply use an instance of Random, and the NextBatch request will invoke the #next: method on it. Corresponding binding definition could look as follows:
With the operation map we can create an instance of a broker for XML over HTTP as follows:
client := (BrokerConfiguration standard adaptor: (AdaptorConfiguration connectionOriented transport: (TransportConfiguration http marshaler: (MarshalerConfiguration xml binding: map))) ) newAtPort: 4243. client start.
Of course we will need to setup a server to be able to run the example. Let's assume we have another XML broker called server running on port 4242. To complete the server setup we just need to register the server object implementing the service with the server broker. Registration is performed by simply exporting the server object with a preselected OID:
service := Random new. server objectAdaptor export: service oid: 'random'.
Now we are ready to actually send requests. XML requests are sent to instances of a URL instead of an ObjRef. The URL should point to the address that the server broker is listening on and include the oid as an additional path element. In our case it could be:
url := 'http://localhost:4242/random' asURI.
A requests can be sent in an RPC style or in a fully transparent way. An RPC style request invocation would looks as follows:
To be able to send a message transparently we need to have a proxy for the remote service (of course the proxy can be reused for multiple message sends):
random := RemoteObject newOn: url requestBroker: client. reply := random next: 10.
Don't forget that when you're done using a broker it should be stopped.
client stop. server stop.
Output Parameter Support
To support the notion of 'output parameters' in remote procedure calls, an instance of OutputParameter has to be passed as a message argument for each output parameter. To create an output parameter use message #asOutputParameter sent to either a nil for plain output parameter, or to the input value in case of input/output parameter. Support for output parameters has following impact on the wire protocol format. Requests have to be a structs with number of elements corresponding to the number of input parameters. If output parameters are not used reply can be anything but struct. If output parameters are used then reply must be a struct with first element corresponding to the reply value and the rest of the struct elements being the output parameter values. So the number of struct elements has to match the number of output parameters plus one. For example, let's take a DivMod operation that returns both the quotient and the remainder of integral division of two integers. Let's assume that result value will be the quotient and the remainder will be returned in an output parameter. This can be modeled using following message
| quotient remainder | remainder := nil asOutputParameter. quotient := aDivModService divmod: a with: b remainder: remainder.
After this the result is in variable quotient and the remainder is the value of the OutputParameter in variable remainder, which can be obtained by sending #value to it. Note also that OutputParameter is a Proxy subclass forwarding any messages it doesn't understand itself to its value, so it can be used completely transparently in many situations. The implementation of the #divmod:by:remainder: method can look like this:
Opentalk-SOAP package integrates generic SOAP/WSDL support with Opentalk-XML to provide transparent messaging access to SOAP services for clients and infrastructure for setting up SOAP servers. It is highly recommended to read avaliable documentation for XMLObjectMarshalers, SOAP, UDDI and Opentalk-XML packages before trying to use this package. Also note that SOAP messages are sent via lower transport level protocols (usually via HTTP), which has to be loaded as well in order to set up a SOAP broker.
SOAP marshaler configuration method is #soap with a required parameter #binding: which takes an instance of a WsdlBinding. Another form of the required parameter is #bindingNamed: that takes a name of a preloaded WSDL binding.
Let's build a simple time service as an example. It will support a single operation Now that will return current timestamp. For the service implementation we will simply use the Timestamp class, and the operation 'Now' will invoke the #now method on it. Corresponding WSDL binding definition could look as follows:
We will need to create another SOAP broker for the server, and let's run it on port 4242. To complete the server setup we just need to register the server object implementing the service with the server broker. Registration is performed by simply exporting the server object with a preselected OID:
service := Timestamp. server objectAdaptor export: service oid: 'timeserver'.
This simple setup allows us to send SOAP requests. SOAP requests are sent to instances of a URL instead of an ObjRef. The URL should point to the address that the server broker is listening on and include the OID as an additional path element. In our example we can run both brokers in the same image in which case the URL would be:
url := 'http://localhost:4242/timeserver' asURI.
A requests can be sent in an RPC style or in a fully transparent way. An RPC style request invocation would looks as follows:
To be able to send a message transparently we need to have a proxy for the remote service (of course the proxy can be reused for multiple message sends):
Don't forget that when you're done using a broker it should be stopped.
client stop. server stop.
Mapping SOAP operations to Smalltalk messages.
If you look closely at the example WSDL specification above, you may notice that there's one element attribute that you won't find in the WSDL specification. This is the attribute 'selector' in the element in the section. This attribute is very important as it is the only thing that links the WSDL operation 'TimeNow' to the smalltalk message #now. Without it the framework can only make some simple guesses about the smalltalk message to invoke when a SOAP request arrives. It is also important for clients, because it instructs the marshaler to marshal given smalltalk message as the corresponding WSDL operation in the outgoing SOAP request. If the selector is not defined in the WSDL operation binding the operation name is transformed using the algorithm in SoapOperationBinding>>equivalentSmalltalkMessageName, the target objects is checked if it responds to the computed selector and that message is sent to it if it does. If the target object does not respond to neither selector it will be sent message #soapPerform: with the SOAPRequest instance as the argument.
It is usually desirable to provide explicit mapping between WSDL operations and smalltalk messages. However WsdlBindings are often built automatically from publicly available binding specifications, and it is hard to expect the specification writers to add valid smalltalk selector equivalents of operation names to it. If simply editing the specification and filling in the 'selector' attribute is not feasible, for example because the specification is still under developement and changes frequently, it is possibly to modify the instance of WsdlBinding directly aftet it is built from the specification XML. For example if the 'TimeNow' operation didn't have the selector attribute specified, we could use following code fragment to compensate for that:
(binding operations detect: [ :op | op name asString = 'TimeNow' ] ) selector: #now.
User defined SOAP types.
Only simple datatypes are passed along with the messages in our simple example. To be able to handle more complex objects the marshaling framework needs an XMLToSmalltalk binding, the same thing as the one used for generic XML brokers. The binding definition can be included directly in the WSDL specification as in the next example:
This is a schema that allows us to setup a server supporting a ConverPolar operation that takes polar coordinates and returns an instance of Point with (x,y) coordinates. The operation binding plugs into the Point class>>r:theta: method, so one can simply export the Point class itself as the service implementation. The client side can directly use the #r:theta: message to invoke the operation.
The standard way to define custom data types using the WSDL section. So instead of the section above there would be something like the following:
'... targetNamespace="urn:coordinate-converter">
...'
This has to be converted into an section for the marshaling framework. It can be done by hand, or with the help of Net.XMLTypesParser, which is able to generate an out of the WSDL section. An example of how to use the XMLTypesParser is the Net.WsdlClient which uses it for "on-the-fly" parsing of WSDL specifications downloaded from the internet. Currently the easiest way to invoke the parser is through the WsdlClient as follows:
Note that the generated by the parser has a limitation though. Since the section doesn't specify which smalltalk classes do the individual types correspond to, it generats all the type marshalers as struct marshalers. This means that the marshalers don't expect to handle general objects but instances of Net.Struct instead. This is actually useful for dynamic clients like WsdlClient where one cannot expect to have the domain classes to be readily implemented in the client image. Marshaling the datatypes as Structs avoids this obstacle.
Exceptions and SOAP Faults
In general, any exceptions sent to the client are sent as SoapFaults. SoapFault instances can be generated by Opentalk or built by the application code. Transparent handling of operation faults (faults listed in the operation declaration in the WSDL specification) is also supported. Operation faults are marshaled into the element of SoapFault. On the client side Opentalk checks if the incoming SoapFault has an operation fault in its element. If there is one, it signals the exception, otherwise it signals the generic SoapFault exception. Operation faults are expected to be implemented as subclasses of Error. The application code generates an operation fault by signaling the corresponding Error. Application code can also generate a SoapFault explicitly by signaling a SoapFault exception. Client code is expected to use the usual exception handling constructs, with the constraint that faults are inherently not resumable.
Output Parameter Support
Output parameters are used the same way with SOAP brokers as with XML brokers (see the package comment for Opentalk-XML), so we will discuss only SOAP specific differences here. In SOAP, output parameters only apply to RPC style, where use of output parameters is indicated by multiple parts in the output message. According to the SOAP specification the first part corresponds to the result value, and any subsequent parts correspond to output parameters (in that specific order). Obviously the number of output parts has to match the number of output parameters plus one (for the result value).
The only SOAP specfic difference in the DivMod example introduced in the Opentalk-XML package comment will be the schema, which in case of SOAP could look something like this:
The Opentalk-SOAP-ServerHeaders package includes classes to support web services invocation with headers on the server side. WsdlServiceProvider is the RequestBroker wrapper and holds the request header repository for the specific Wsdl binding port. The header demo can be loaded from the Opentalk-SOAP-HeadersDemo. In this demo the CustomerServer class implements the Opentalk server with services that are collection of instances of WsdlServiceProvider.
The WsdlServiceProvider creates an instance of the ServerOperationHeaderProcessor class for each request. This object holds request header entry state and will call request/response header entry processors. The ProcessingPolicy implements the header entry processing policy: 1. if a header entry has mustUnderstand=1 or mustUnderstand=0 attribute, targeted at ProcessingPolicy>>role and has a header entry processor, the header entry will be processed with result that header entry is returned to client 2. if a header entry has mustUnderstand=0 attribute, targeted at ProcessingPolicy>>role and doesn't have a header entry processor, the header entry will be ignored 3. if a header entry has mustUnderstand=1 attribute, targeted at ProcessingPolicy>>role and doesn't have a header entry processor, the SoapMustUnderstandFault will be raised 4. if a header entry is not targeted at ProcessingPolicy>>role it will be ignored
ServerHeaderEntryProcessor is superclass for the Opentalk server specific header entry processors. Subclasses must implement actions on header entries when a request is received or reply is sent. A specific header entry processor has to have #headerName class method that returns the header node tag described by namespace and type. Subclasses must implement the following messages: processing headerEntryFor:transport: processHeaderFrom: transport:
Opentalk client header support.
The Opentalk-SOAP-Client package includes classes to support web services invocation with headers on the client side. WsdlServiceConsumer - is abstract superclass for the user specific client class. The class holds request header repository for the Wsdl binding. A customer should create a client class as subclass of WsdlServiceConsumer and implement the following methods: #binding #port #serverUrl The 'public api' category has to include all methods to access web services. The header demo can be loaded from the Opentalk-SOAP-HeadersDemo. In this demo the CustomerClient implements the user specific client.
ClientHeaderEntryProcessor - is superclass for the user specific header entry processors. The class implements the default action on header entry when a request is sent or response is received. A specific header entry processor has to have #headerName class method that returns the header node tag described by namespace and type. The header entry processors are optional. The default header entry processor adds necessary header entries from the WsdlServiceConsumer repository to the request. If there is no header entry but the Wsdl schema describes a header entries for the operation the MissingRequestHeader exception will be raised. When the response is received the default header entry processor does not perform any action on this header entry. If the response header is missing some header entries the MissingResponseHeader exception will be raised. While sending the request the specific header entry processor can override the default behavior in the method: ClientHeaderEntryProcessor>>processHeaderFrom:transport:
When the response is received the specific header entry processor can add some processing to the header entry in the method: ClientHeaderEntryProcessor>>process:reply:transport:
The header entry can be added to the request in two ways: 1. to client repository: client := Smalltalk.CustomerClient new. client start. (client headerFor: #AuthenticationToken) value: ( AuthenticationToken new userID: 'UserID'; password: 'password'; yourself).
2. in header entry processor CHPAuthenticationID>>processHeaderFrom: aSOAPRequest transport: aTransport self headerEntry value isNil ifTrue: [(aSOAPRequest headerFor: #AuthenticationID) value: (WebServices.Struct new id: 'ID#1234'; yourself)] ifFalse: [ super processHeaderFrom: aSOAPRequest transport: aTransport ]
The result of the client service invocation can be set up to return 1. a value - which the body value. This is default setting. SOAPMarshaler defaultReplyReturnValue: #value. access to response header will available only in client header entry processors
2. an envelope - which is instance of WebServices.SoapEnvelope SOAPMarshaler defaultReplyReturnValue: #envelope having the envelope as the result allows to get access to response header and body
The WsdlServiceConsumer creates an instance of the ClientOperationHeaderProcessor class for each request. This object holds request header entry state and will call response header entry processors when the response arrives.
PackageDescription: Opentalk-Web
Here's a simple secure web (The resource is from the X509Tests package)
This package tests XML marshaling with HTTP transport. The tests are built around a simple unit conversion service represented (surprisingly enough) by class UnitConverterService. The idea is that the service maintains a collection of Unit definitions for different kinds of Measures. Units that belong to the same Measure can be converted from one to the other using the defined ratio to the 'base' Unit of the Measure. The service also allows to add and remove Unit and Measure definitions. The XMLToSmalltalk schema is defined on the class side of UnitConverterService.
The package includes classes to demostrate how to use Soap headers on Opentalk client and server
PackageDescription: Opentalk-SOAP-Tests
This is the test package for Opentalk-SOAP. It uses Opentalk-HTTP as the transport layer. Several kinds of domain schemas are used. UDDI is used in SoapTest and UddiTest. Simple DateCalculator schema is used for basic testing in DateCalculatorTest. Finally UnitConverter schema (from Opentalk-XML-Tests) is used to test scenarios with user defined types and exceptions.
These tests require an external HTTP web server configured with the VisualWave CGI gateway. The server obviously doesn't have to run on the same machine as the test image. To make running these tests as simple as possible, they are written so that both client and server brokers are running in the same image. Each request is sent by the client broker goes to the web-server and then through the VisualWave CGI gateway back to the server broker port.
There is an IMPORTANT setup step that must be completed before the tests can run. The step is setting the server URL. The default setting expects the webserver to run on the same machine and the CGI gateway executable to be named using the host@port convention as 'localhost@4242'. If this doesn't match your setup a correct URL has to be set using
In case the gateway executable doesn't use the host@port naming convention (i.e. the VisualWave gateway hostmap filed is used), then the server broker port number may have to be specified using
DateCalculatorCGITestResource serverPort: 5555
so that the port number matches the one specified in the hostmap file. The default server port number is 4242 so if the hostmap file uses this number the server port doesn't have to be set explicitly.
Apache 1.3/RedHat 7.1
Here's what I did for my setup (all steps are performed as root). 1) Copied the Linux CGI gateway executable to the /var/www/cgi-bin directory and renamed it to laptop@4242. Then changed it's group to 'apache' (the group that the Apache is running in) and chmod g+x. 2) Created directory /etc/VisualWave and put the default cgi2vw.ini in it. Made both the directory and the file readable by the 'apache' group. 3) Added the following line to the /etc/httpd/conf/httpd.conf file (so that the gateway can find the INI file): SetEnv SystemRoot /etc 4) Finally started the server with /etc/init.d/httpd start
In the image I had to set the serverUrl as shown in the example above. And then I could run the tests using: