Building XML Server applications
Communication via Web Server Interface (WSI)
The ST: XML Communications feature extends the Web Server Interface (WSI) layer of Web Connection to support XML and handle the incoming server requests. VAST Platform (VA Smalltalk) XML support provides the AbtXmlWsiHandler, which is a subclass of AbtWsiHandler. You can subclass the AbtXmlWsiHandler to create custom XML request handlers. You must then override the abtWsiHandleWsiTransaction: method in order to process incoming XML requests.
VAST Platform provides a Part type named XML Request Handler, in the New Part view to simplify the creation of AbtXmlWsiHandler subclasses.
VAST Platform provides a transport called xml-tcp that allows applications to process socket requests without first passing them through an HTTP server. To use the xml-tcp server transport, perform the following steps:
1. Start a WSI Server... window of the WSI monitor
2. Specify xml-tcp for the Transport.
3. Specify a handler class name in the Request Handler. The handler class is then responsible for processing socket requests via its abtWsiHandleWsiTransaction: method.
For more information on setting up and using the Web Server Interface, see the Web Connection User Guide.
For Linux (UNIX):
The XML server sample named AbtXmlSampleCustomerRequestHandler contains code with hard coded directory references that do not resolve properly. If you would like to test this sample, you must first do the following:
a. Create a subdirectory named xml in the development folder of your VAST Platform environment. For example, for a 64-bit Linux installation :
mkdir xml
b. Copy the xml files from the VAST Platform installation directory /usr/local/VASTPlatform/12.0.0x64/xml to the newly created xml directory. For example:
cd <vast client directory>/xml
cp /usr/local/VASTPlatform/12.0.0x64/xml/*.* .
Starting an xml-tcp transport from a configuration file
To start an xml-tcp transport from a configuration file, you must specify a handlerClassName in the configuration file. The format of the configuration file for xml-tcp is as follows:
transport xml-tcp
port 8081
showMonitor true
handlerClassName AbtXmlWsiHandler
monitorTitle Show me the money!
generateWalkback true
XML Server Samples
Creating an XML HTTP request handler using the XML DOM parser
This section steps you through the process of building a sample server application. This application is a simple illustration of a customer request handler that uses an XML DOM parser (AbtSampleCustomerRequestHandler). The main steps to create this sample are as follows:
•Create the AbtSampleCustomerRequestHandler as a subclass of the AbtXmlWsiHandler class.
a. In the Organizer, create a new part.
b. For Part type, select XML Request Handler.
c. For Part class, type the name of your new class (ex. AbtXmlSampleCustomerRequestHandler).
•Write a method to handle the customer transaction. The #abtHandleWsiTransaction: sample method does the following:
a. Obtains the XML string from the transaction object.
b. Parses the XML string using the DOM parser
c. Uses the input deserialization API to convert the DOM into an AbtXmlSampleCustomerRequest object.
d. Processes the command specified in the customer request object.
e. Creates an AbtXmlSampleCustomerResponse object and write its XML representation to the response of the AbtWsiTransaction using the output serialization method #abtXmlPrintString.
abtHandleWsiTransaction: anAbtWsiTransaction
" Handle the incoming AbtWsiTransaction. The expected content is an
AbtXmlSampleCustomerRequest passed as XML. This method performs the following steps.
1) Obtain the XML string from the transaction object.
2) Parse the XML string using the DOM parser
3) Use input deserialization API to convert the DOM into an AbtXmlSampleCustomerRequest
object
4) Process the command specified in the customer request object
5) Create an AbtXmlSampleCustomerResponse object and write its XML representation
to the 'response' of the AbtWsiTransaction using output serialization method
#abtXmlPrintString "
| xmlString customerRequest domDocument |
" Get the XML request string from the passed transaction object "
xmlString := anAbtWsiTransaction request content.
" Parse the XML string while handling any exceptions that occur during parsing "
self handleParseExceptionsWhile: [
domDocument := AbtXmlDOMParser newNonValidatingParser parse: xmlString ].
" Construct a customerRequest object from the contents of the incoming domDocument.
This requires that the contents of the DOM be 'mapped' into the desired object "
domDocument notNil
ifTrue: [customerRequest := self customerFromDOM: domDocument ].
" An error occurred converting the DOM into a customer object. Pass back some XML to
report the error "
customerRequest isNil
ifTrue: [ anAbtWsiTransaction response content: self createErrorString contentType:
'text/xml' ]
ifFalse: [
anAbtWsiTransaction response content:
(self processCustomerRequest: customerRequest ) contentType: 'text/xml' ]
•Write a method to create a customer object from the DOM. The customerFromDOM: sample method constructs a customer request object from the contents of an incoming DOM. This method requires that the contents of the DOM be mapped into the desired object.
This method also checks the object cache for a mapping specification. If a mapping specification is not found, a new mapping specification is created from the external mapping file and cached to the object cache.
customerFromDOM: aDocumentObjectModel
" Create a new instance of AbtXmlSampleCustomer using the passed DOM "
| mappingDOM mappingSpec object mapFilePath sep |
" Check the object cache for the presence of the mappingSpec. If it is not found,
create a new mappingSpec from the external mapping file, and add the resulting
mappingSpec to the objectCache for future usage "
( mappingSpec := AbtXmlObjectCache current mappingSpecNamed: self mappingSpecName ) isNil
ifTrue: [
sep := AbtXmlResourceReader current pathSeparator asString.
mapFilePath := '..', sep, 'xml', sep, 'samples', sep, 'customer.map'.
self handleParseExceptionsWhile: [
mappingDOM := AbtXmlDOMParser newValidatingParser parseURI: mapFilePath ].
self handleDOMExceptionsWhile: [
mappingSpec := AbtXmlMappingSpec fromMappingDOM: mappingDOM ].
mappingSpec notNil
ifTrue: [ mappingSpec addToXmlObjectCache ] ].
mappingSpec notNil
ifTrue: [ object := aDocumentObjectModel mapUsing: mappingSpec ].
^object
•Write a method to process the customer request. The processCustomerRequest: sample method processed the command described in the incoming customer request.
processCustomerRequest: anAbtXmlSampleCustomerRequest
" Process the command described in the passed customer request "
| customerResponse customer |
anAbtXmlSampleCustomerRequest isNil
ifTrue: [ ^self createErrorString ].
customerResponse := AbtXmlSampleCustomerResponse new.
" Simple application code for demonstration purposes "
anAbtXmlSampleCustomerRequest request = 'INQUIRY'
ifTrue: [
customerResponse customer: anAbtXmlSampleCustomerRequest customer.
customerResponse customer lastName = 'Programmer'
ifTrue: [
customerResponse customer
firstName: 'Joe' ;
lastName: 'Programmer' ;
email: 'joeprogrammer@instantiations.com' ;
streetAddress: '100 Instantiations Way' ;
city: 'Portland' ;
state: 'Oregon' ;
zip: '97224'.
customerResponse response: 'Success' ]
ifFalse: [ customerResponse response: 'Not found' ] ]
ifFalse: [
customerResponse response: 'Not supported' ].
^customerResponse abtXmlPrintString
•Write a method to handle the DOM exceptions. The handleDOMExceptions sample method handles any exceptions that occur while manipulating DOM objects:
handleDOMExceptionsWhile: aBlock
" Execute the passed block wrapped in code to handle exceptions that occur
while manipulating DOM objects"
aBlock
when: AbtDOMConstants::AbtDOMException
do: [ :aSignal |
self logError: aSignal argument printString
]
•Write a method to handle parser exceptions. The handleParseExceptionsWhile method handles SGML exceptions:
handleParseExceptionsWhile: aBlock
" Execute the passed block wrapped in code to handle SGML exceptions "
aBlock
when: SgmlExceptions::SgmlException
do: [ :aSignal |
self logError: aSignal argument printString
].
Creating an XML HTTP request handler using the SAX parser
This section steps you through the process of building a sample customer request handler that uses a SAX parser.
Create the AbtSampleSaxCustomerRequestWsiHandler as a subclass of the AbtXmlSampleCustomerRequestHandler class. In the Organizer, create a new part. For Part type, select XML Request Handler. For Part class, type the name of your new class (ex. AbtXmlSampleSaxCustomerRequestWsiHandler).
Write a method to override the #abtHandleWsiTransaction: method and handle the customer transaction using a SAX parser. The #abtHandleWsiTransaction: sample method does the following:
a. Obtains the XML string from the transaction object.
b. Parses the XML string using the SAX parser and custom SAX handler named AbtXmlSamplesSaxCustomerContentHandler.
c. Processes the command specified in the resulting customer request object.
d. Creates an AbtXmlSampleCustomerResponse object and write its XML representation to the response of the AbtWsiTransaction using output serialization method #abtXmlPrintString.
abtHandleWsiTransaction: anAbtWsiTransaction
" Handle the incoming AbtWsiTransaction. The expected content is an
AbtXmlSampleCustomerRequest
passed as XML. This method performs the following steps.
1) Obtain the XML string from the transaction object.
2) Parse the XML string using the SAX parser and custom SAX handler named
'AbtXmlSamplesSaxCustomerContentHandler'
3) Process the command specified in the resulting customer request object
4) Create an AbtXmlSampleCustomerResponse object and write its XML representation
to the 'response' of the AbtWsiTransaction using output serialization method
#abtXmlPrintString "
| xmlString customerRequest |
" Get the XML request string from the passed transaction object "
xmlString := anAbtWsiTransaction request content.
" Parse the XML string while handling any exceptions that occur during parsing "
self handleParseExceptionsWhile: [
customerRequest := ( AbtXmlSaxParser newNonValidatingParser contentHandler:
AbtXmlSampleSaxCustomerContentHandler new )
parse: xmlString ].
customerRequest isNil
ifTrue: [ anAbtWsiTransaction response content: self createErrorString contentType:
'text/xml' ]
ifFalse: [
anAbtWsiTransaction response content:
(self processCustomerRequest: customerRequest ) contentType: 'text/xml' ]
Create a default handler for the SAX parser. The AbtXmlSampleSaxCustomerContentHandler sample class is a subclass of the AbtXmlSaxDefaultHandler class.
Application Prerequisites
Before you package an application, make sure that the prerequisites are defined. The following list shows the XML support applications and the features they provide:
AbtXmlDOMParserApp
For applications that parse XML to create a document object model (DOM) object.
AbtXmlSaxParserApp
For applications that parse XML using a SAX parser with customized SAX handlers.
AbtXmlSerializationApp
For applications that use the input and/or output serialization API.
AbtXmlServerInterfaceTcpApp
For applications that use the WSI extensions for XML to perform XML transaction processing.
AbtXmlServerInterfaceViewTcpApp
For applications that use the Web Server Interface Monitor UI to manage WSI connections.
Packaging the image with XML image components
If you wish to package your XML application so that it utilizes the XML image components, you must implement a packager method to force inclusion of your XML request handler parts in the packaged image. For example, you can implement the following method as a class method of the application containing your XML request handler parts:
packagerIncludeClassNames
| handlers |
handlers := AbtXmlServerSamplesApp defined select: [:aClass |
aClass inheritsFrom: AbtXmlWsiHandler ].
^handlers collect: :aClass | aClass name ]
For reduced runtime images that are packaged without the XML image components, all XML request handlers are automatically included in the packaged image.
Note:
The Web Connection feature must be loaded in order to package an XML application that utilizes the WSI communications features.
Packaging considerations when using XML input deserialization
If the input deserialization functionality of the XML feature is used to map incoming XML requests into Smalltalk business objects, the packager will exclude classes that are not specifically referenced in code. The specific sequence of events that may cause packaging concerns is as follows:
•Parse an XML file to construct a DOM (document object model).
•Send the #mapUsing: method to the DOM and pass it a valid instance of the AbtXmlMappingSpec class.
The #mapUsing: API uses the passed mapping specification to build an object from the contents of the DOM. However, the steps above do not cause any actual references to the class name of the constructed instance. Therefore, packaging rules must be used to instruct the packager that unreferenced classes should be included in the packaged image.
For example, the #packagerIncludeClassNames packager method (a class method) can be implemented in the application class. For example, if MyModel1 and MyModel2' are classes in MyApplication, then the method below should be implemented as a class method in MyApplication.
packagerIncludeClassNames
" Include class names that might be constructed via XML mapping "
^#(MyModel1 MyModel2)