Data Transport Standard v 1.0 Reference Implementation Guide

Data Transport Standard v 1.0
Reference Implementation Guide
Version 1.0
February 6, 2006
Prepared by
PESC DTS Technical Workgroup
© Postsecondary Electronic Standards Council (PESC) 2005. All Rights Reserved.
This document may be copied and furnished to others, and derivative works that comment on
or otherwise explain it or assist in its implementation may be prepared, copied, published and
distributed, in whole or in part, without restriction of any kind, provided that the above
copyright notice and this paragraph are included on all such copies and derivative works. This
document itself, however, may not be modified in any way except when expressly approved by
PESC for the purpose of developing standards and specifications.
Notice:
The material contained herein is not a license, either express or implied, to any intellectual
property owned or controlled by any of the authors or developers of this material or PESC. The
material contained herein is provided on an “AS IS” basis and to the maximum extent
permitted by applicable law, this material is provided AS IS AND WITH ALL FAULTS, and the
authors and developers of this material and PESC hereby disclaim all other warranties and
conditions, either express, implied or statutory.
IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THIS MATERIAL OR PESC BE
LIABLE TO ANY OTHER PARTY FOR THE COST OF PROCURING SUBSTITUTES GOODS
OR SERVICES, LOST PROFITS, LOSS OF USE, LOSS OF DATA, OR ANY INCIDENTAL,
CONSEQUENTIAL, DIRECT, INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER
CONTRACT, TORT, WARRANTY, OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS
OR ANY OTHER AGREEMENT RELATING TO THIS MATERIAL, WHETHER OR NOT
SUCH PARTY HAD ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
Table of Contents
TABLE OF CONTENTS ............................................................................................................................................3
1
INTRODUCTION ................................................................................................................................................4
1.1
1.2
1.3
1.4
1.5
1.6
1.7
DOCUMENT DESCRIPTION.............................................................................................................................4
PURPOSE .......................................................................................................................................................4
TERMINOLOGY .............................................................................................................................................4
TOOLSETS USED TO CREATE REFERENCE IMPLEMENTATIONS ......................................................................4
.NET .........................................................................................................................................................4
JAVA ........................................................................................................................................................4
PRIVATE AND PUBLIC KEY HANDLING .........................................................................................................4
COMPRESSION AND ENCODING ......................................................................................................... …….. 4
DISCLAIMERS ...............................................................................................................................................5
2
INTERFACES ..................................................................................... ERROR! BOOKMARK NOT DEFINED.
3
REFERENCE IMPLEMENTATION UTILITY CLASSES ............................................................................9
4
STEPS FOR CREATING A DTS CORE CLIENT ......................................................................................... 10
5
STEPS FOR CREATING A DTS CORE SERVICE ...................................................................................... 13
6
SERVICE DEPLOYMENT CONSIDERATIONS .......................................................................................... 15
6.1
6.2
.NET CONSIDERATIONS.............................................................................................................................. 15
JAVA CONSIDERATIONS - WSDD (WEB SERVICES DESCRIPTION DEFINITION) ......................................... 15
Java Considerations - Packaging and third party utilities ..................................................................... 16
7
NET CORE CLIENT EXAMPLES .................................................................................................................. 17
8
NET CORE SERVICE EXAMPLES................................................................................................................ 23
9
JAVA DTS CORE CLIENT EXAMPLES ....................................................................................................... 32
10 JAVA DTS CORE SERVICE EXAMPLES .................................................................................................... 40
REVISION HISTORY .............................................................................................................................................. 45
Version: 1.0
02/17/2016
Status: Approved
Page 3
1
INTRODUCTION
1.1
Document Description
This document is a supplemental document to the DTS Technical Specification Document. It is
to be used as a guide and a reference to implement the DTS specification. The descriptions and
examples contained in this document are taken from the reference implementations created by
the DTS Technical workgroup to assist in writing the DTS specification.
1.2
Purpose
The purpose of this document is to help entities understand the reference implementations of
DTS.
1.3
Terminology
Serialization:
Serialization is the conversion of an object instance to a data stream of byte values. In the case
of Web Services it is the conversion of an object instance to a XML data stream.
De-serialization:
De-serialization is the conversion of a data stream of byte values to an object instance. In the
case of Web Services it is the conversion of a XML data stream to an object instance.
1.4
Toolsets Used to Create Reference Implementations
.NET





.Net Framework 1.1
WSE 1.0
IIS 5 or later
J# Redistributable Runtime/SharpZipLib (compression routines)
Visual Studio .Net 2003
JAVA




Apache Axis 1.1 web service toolkit
Apache Jakarta Tomcat 4.x or higher
Sun Java JRE 1.4 (SDK for Development)
A Java IDE (Eclipse, JDeveloper)
1.5
Private and Public Key Handling
Digital signing and verifying signatures are required processing for both a DTS Service and a
DTS Client. This is the primary means for DTS to authenticate the sender of the data. Signing
and verifying require the use of X.509 certificates. Private keys will be maintained locally at
each organization and not shared with other organizations. Public keys will need to be
distributed to each organization that produces or consumes a DTS service. Currently there are
options under review by the DTS Business workgroup to determine the best way for DTS
Version: 1.0
Status: Approved
February 2006
Page 4
participants to obtain public keys. The reference implementations have provided a simple
interface and a generic implementation of that interface to obtain the public keys required for
the reference implementations to work. The IPubicKeyHandler interface provides the means
for the reference implementation to obtain a generic Public key that allows for the verification of
the digital signatures. Once the DTS Business workgroup determines a method for exchanging
public keys, each organization will need to create its own method to meet the requirements of
the DTS business workgroup.
1.6
Compression and Encoding
To reduce the amount of data being transmitted over the wire, the DTS Client Request and DTS
Service Response need to be zlib compressed. The compressed payload also needs to be base64
encoded to ensure that special characters get translated correctly when transmitted over the
wire.
1.7
Disclaimers
The reference implementations are one way to implement the specification. Entities may differ
in their implementation approach as various tool sets evolve.
Version: 1.0
02/17/2016
Status: Approved
Page 5
2
INTERFACES:
Best practices suggest the use of Interfaces in object oriented design to ensure that all of the
functionality of a particular unit of work is being completed successfully. Implementation of
Interfaces varies depending on programming language and platform. Below is a list of the key
Interfaces of the reference implementation used to meet the DTS specification.

A DTS Core Client should implement the following Interface:
IDTSCoreClient
Method Name
Return Value
Arguments/Types
Note
The Set methods must be
called prior to calling the
invoke method
setSourceId
void
sourceid/String
Routing for Service
setSourceIdSubCode
void
sourceidsubcode/String
Routing for Service
setRecipientId
void
recipientid/String
Routing for Service
setRecipientIdSubCode
void
recipeintidsubcode/String
Routing for Service
setUUID
void
uuid/String
Routing for Service
setEndpoint
void
setPrivateKey
void
endpoint/String
privatekey/Platform
Specific
Service Endpoint
Set prior to invoke for
signing of the payload
setPayloadType
void
payloadtype/String
Set prior to invoke
setServiceExpectation
void
serviceexpectation/String
Set prior to invoke
setPayload
void
payload/String
setPublicKeyHandler
void
handler/IPublicKeyHandler
Set prior to invoke
Set prior to invoke for
verifying signature
invoke
void
N/A
throws Exception
The Get methods should
be read after the invoke
method is called
getResponse
String
N/A
Routing from Service
getSourceId
String
N/A
Routing from Service
getSourceIdSubCode
String
N/A
Routing from Service
getRecipientId
String
N/A
Routing from Service
getRecipientIdSubCode
String
N/A
Routing from Service
Version: 1.0
Status: Approved
February 2006
Page 6
getUUID
String
N/A
Routing from Service
getTransmissionDateTime
String
N/A
Routing from Service
getResponseType
String
N/A
getResponseAcknowledge
String
N/A
Read for Response Type
Read for Response
Acknowledge

Both the DTS Core Client and DTS Core Service have requirements for verifying digital
signatures. Both cores will need to have their corresponding application components assign
an object that meets the following interface to be able to obtain the appropriate public key
for verification. This interface is subject to change based on any decisions that the DTS
business workgroup makes about key exchange.
IPublicKeyHandler
Method Name
getPublicKey
getPublicKey

Return Value
publickey/Platform
Specific
publickey/Platform
Specific
Arguments/Types
sourceId/String
sourceIdSubCode/String
sourceId/String
payloadType/String
sourceIdSubCode/String
Note
The DTS Core Service might instantiate a DTS Service Application object that implements
the following interface:
IDTSServiceApplication
Method Name
Return Value
Arguments/Types
Note
setSourceId
void
sourceid/String
Routing from Client
setSourceIdSubCode
void
sourceidsubcode/String
Routing from Client
setRecipientId
void
recipientid/String
Routing from Client
setRecipientIdSubCode
void
recipeintidsubcode/String
Routing from Client
setUUID
void
uuid/String
Routing from Client
setPayloadType
void
payloadtype/String
Set before invoke
setServiceExpectation
void
serviceexpectation/String
Set before invoke
setPayload
void
payload/String
Set before invoke
setTransmissionDateTime
void
transmissiondatetime/String Set before invoke
invoke
void
N/A
getPublicKeyHandler
void
getPrivateKey
void
Version: 1.0
02/17/2016
throws Exception
Read prior to invoke for
handler/IPublicKeyHandler validating signature
Read after invoke for
privatekey/Platform Specific signing the response
Status: Approved
Page 7
getResponse
String
N/A
Read after invoke
getSourceId
String
N/A
Routing for Client
getSourceIdSubCode
String
N/A
Routing for Client
getRecipientId
String
N/A
Routing for Client
getRecipientIdSubCode
String
N/A
Routing for Client
getUUID
String
N/A
getPayloadType
String
N/A
getResponseAcknowledge
String
N/A
Routing for Client
Read after invoke for
Response Type
Read after invoke for
Reponse Acknowledge
Version: 1.0
Status: Approved
February 2006
Page 8
3
REFERENCE IMPLEMENTATION UTILITY CLASSES
JAVA
Logging
Zlib
PublicKeyHandler
.Net
Compression
Certificates
Version: 1.0
- Logging utility class.
- Provides wrapper to the actual java zlib classes.
- This object implements the IPublicKeyHandler interface and returns a
generic public key for the reference implementation.
- Provides wrapper to J# zlib classes or to the SharpZipLib.
- Provides generic method for getting RSA Cryptographic Service
Provider from an X.509 certificate in the computer store or file.
02/17/2016
Status: Approved
Page 9
4
STEPS FOR CREATING A DTS CORE CLIENT
1. Error reporting to DTS Client Application prior to calling the service:
The DTS Core Client should perform checks before calling the service to ensure that all
required elements are present as per the specification. Exceptions should be raised back
to the DTS Application Client if elements are missing.
2. Create container classes for header elements:
Container classes need to be created for each header element defined in the WSDL. The
container classes provide the holding area for data to be serialized and de-serialized to
and from the SOAP.
3. Directives for serialization/de-serialization:
Both .Net and Java (Apache Axis) need to define how the container classes should be
serialized and de-serialized. Java requires a qualified name attribute and namespace
attribute to be associated with each header element. These attributes are added during
the call to the platform specific directives. Without the directives the data within the
container classes cannot be added or extracted from the SOAP through the respective
toolkits.
4. Preparing the payload:
The DTS Core Client is required to zlib compress and base64 encode the payload. To
reduce the amount of data being transmitted over the wire the payload needs to be zlib
compressed. The compressed payload also needs to be base64 encoded to ensure that
special characters get translated correctly when transmitted over the wire.
5. Signing the payload:
The DTS Core Client is required to digitally sign the compressed and encoded payload
with an X.509 certificate private key. The DTS Client Application is responsible for
setting a Private Key object on the DTS Core Client so it can sign the prepared payload.
The DTS Core Client is also required to base64 encode the signature prior to putting it
into the DTSRequestSignature SOAP Header element.
6. Preparing the DTS Request Headers:
Each request header container class must be instantiated and element values set
correctly. The Core Client then needs to create a SOAPHeader element for each request
header object to be added to the SOAP. The following is the full list of request header
elements that need to be created:
DTSRequestRouting
DTSRequestPayloadType
DTSRequestServiceExpectation
DTSRequestPayloadBytes
See the “types” section of the DTS WSDL for a complete definition of these header
elements.
Version: 1.0
Status: Approved
February 2006
Page 10
7. Invoking the service:
The DTS Core Client is required to invoke a call to the DTS Service and wait for the DTS
Core Service response.
8. Handling of SOAP faults returned from DTS Core Service:
The DTS Core Client is required to handle any SOAP faults raised by the DTS Core
Service. These will be returned as the body of the response. There are no requirements
to do anything special at the DTS Core Client level except to reformat the SOAP fault
into an application exception that the DTS Client Application can use. Depending on
the requirements of each organization and depending on the organization’s
implementation of the DTS Core Client, logging and notification processing may need to
be added. At a minimum, the exception returned to the DTS Client Application can be
interrogated and processed.
See the DTS Specification for the exact fault information to be sent back.
9. Handling of communication or framework errors generated outside of DTS:
The DTS Core Client should handle any platform or protocol specific errors not
explicitly generated by a DTS Core Service. The current reference implementations
simply raise these errors back to the DTS Client Application.
10. DTS Response header processing:
If no SOAP exceptions are received from the DTS Core Service, the DTS Core Client
should continue processing by extracting the DTS Response header information and
making it available to the DTS Client Application through the interface defined in the
Interfaces section. The following is the full list of response header elements that need to
be returned from the DTS Core Service:
DTSResponseRouting
DTSResponsePayloadType
DTSResponseAcknowledge
DTSResponsePayloadBytes
See the “types” section of the DTS WSDL for a complete definition of these header
elements.
11. Verifying the response signature:
The DTS Core Client is required to verify the response signature. The DTS Core Client
should instantiate a class that implements the IPublicKeyHandler interface listed above
and call one of the getPublicKey methods to obtain the public key based on the
information contained in the DTS Response header(s). Once the public key is obtained
the DTS Core Client should try to verify the digital signature contained in the DTS
Response Signature header element. If the signature verifies, the DTS Core Client
returns control to the DTS Client Application. If the signature does not verify, an
exception should be thrown to the DTS Client Application.
**** This needs to be removed after the .Net ref implementation matches the doc ******
Note: In the case of the current reference implementations the instantiation of an
IPublicKeyHandler has not been implemented. But for production systems that require
Version: 1.0
02/17/2016
Status: Approved
Page 11
access to multiple public keys, possibly obtained from multiple locations, the
implementation of an IPublicKeyHandler object will facilitate retrieving the appropriate
public keys for the DTS Core Client to use without the DTS Core Client having
knowledge of where the public keys are stored.
12. Response processing:
The response string returned from the DTS Core Service will be base64 encoded and zlib
compressed. It is the responsibility of the DTS Core Client to decode and uncompress
the payload and make it available to the DTS Client Application via the interface.
Version: 1.0
Status: Approved
February 2006
Page 12
5
STEPS FOR CREATING A DTS CORE SERVICE
1. Create container classes for header elements:
Container classes need to be created for each header element defined in the WSDL. The
container classes provide the holding area for data to be serialized and de-serialized to
and from the SOAP.
2. Directives for serialization/de-serialization:
Both .Net and Java (Apache Axis) need to define how the container classes should be
serialized and de-serialized. Java requires a qualified name attribute and namespace
attribute to be associated with each header element. These attributes are added during
the call to the platform specific directives. Without the directives the data within the
container classes cannot be added or extracted from the SOAP through the respective
toolkits.
3. DTS Request Header processing:
In the Java reference implementation, the request header processing is performed by the
RequestHeaderHandler object prior to the service being called. This is the definition of
how the handlers are called and what order is done through the Axis WSDD
configuration file. The RequestHeaderHandler is responsible for performing the
appropriate error handling as per the specification on the request header elements and
creating SOAP faults as needed. Information contained in the request header elements
is passed to the service via parameters set on the MessageContext object.
In the .Net reference implementation, the request header processing is done inside the
asmx file. The objects are de-serialized and accessible by the web service as native
objects to that class.
4. Instantiate the appropriate DTS Service Application:
The appropriate DTS Service Application for the web service being built needs to be
instantiated. The DTS Service Application should meet the IDTSServiceApplication
interface.
5. Verifying the request signature:
The DTS Core Service is required to verify the request signature. The DTS Core Service
should instantiate a class that implements the IPublicKeyHandler interface listed above
and call one of the getPublicKey methods to obtain the public key based on the
information contained in the DTS Request header(s). Once the public key is obtained
the DTS Core Service should try to verify the digital signature contained in the DTS
Request Signature header element. If the signature verifies, the DTS Core Service
continues processing. If the signature does not verify, a SOAP fault should be thrown to
the DTS Core Client.
See the DTS Specification for the exact fault information to be sent back.
Version: 1.0
02/17/2016
Status: Approved
Page 13
6. Payload processing:
The DTS Core Service is required to base64 decode and zlib uncompress the payload
and set it in the DTS Service Application, see Interface section.
7. Invoking a DTS Service Application:
The DTS Core Service is required to set all of the IDTSServiceApplication values,
including the payload, prior to calling the invoke method.
8. DTS Service Application exception handling:
The DTS Core Service is required to handle exceptions thrown from the DTS Service
Application. If the DTS Core Service receives a platform specific exception from the DTS
Service Application it should create and prepare a SOAP fault to be sent back to the DTS
Core Client.
See the DTS specification for the exact fault information to be sent back.
9. Preparing the response:
The DTS Core Service is required to zlib compress and base64 encode the response
retrieved from the DTS Service Application.
10. Signing the response:
The DTS Core Service is required to digitally sign the response with an X.509 certificate
private key. The DTS Service Application is responsible for providing the Private Key
object to the DTS Core Service through an interface so the DTS Core Service can sign the
prepared response. The DTS Core Service is also required to base64 encode the signature
prior to setting it into the DTSResponseSignature SOAP Header element.
11. Preparing the Response Headers:
Each response header container class must be instantiated and element values set
correctly. The Core Service then needs to create a SOAPHeader element for each
response header object to be added to the SOAP. The following is the full list of
response header elements that need to be created:
DTSResponseRouting
DTSResponsePayloadType
DTSResponseAcknowledge
DTSResponsePayloadBytes
See the “types” section of the DTS WSDL for a complete definition of these header elements.
Version: 1.0
Status: Approved
February 2006
Page 14
6
SERVICE DEPLOYMENT CONSIDERATIONS
6.1
.NET Considerations
Organizations should deploy the DTS web service as any other .Net web application. Any
dependency, structure, and deployment issues that have been encountered in other applications
should also be considered for DTS. The .Net reference implementation has been tested under
IIS 5.1 and built using Visual Studio .Net 2003 and the .Net Framework 1.1. The utility classes
mentioned in this document or utility classes built independently must be added to the Global
Assembly Cache or placed in the bin directory of the service being deployed.
6.2
JAVA Considerations - WSDD (Web Services Description Definition)
The Java reference implementation has been tested in the Apache Jakarta Tomcat environment.
Axis requires a WSDD file to be deployed with every install. This file describes the services
available and other important items, i.e. Serialization and De-Serialization directives, pre and
post handler information, and general service properties. The current Java reference
implementation uses pre and post handlers to process the DTS request and response headers.
The current Java reference implementation includes a complete and valid webapps structure
along with a valid WSDD file.
Below is the actual xml for creating references to handler classes in the WSDD:
<handler name="RequestHeaderHandler"
type="java:com.datatransportstandard.referenceimplementation.service.RequestHeaderHandler"/>
<handler name="ResponseHeaderHandler"
type="java:com.datatransportstandard.referenceimplementation.service.ResponseHeaderHandler"/>
Inside the service element you can define how you want Axis to chain the handlers and the
service together. Adding the two elements below instructs Axis to call the handler(s) identified
in the <requestFlow>, then the service class and then the handler(s) identified in the
<responseFlow>.
Below is the actual xml for creating request and response flows in the WSDD:
<requestFlow>
<handler type="RequestHeaderHandler"/>
</requestFlow>
<responseFlow>
<handler type="ResponseHeaderHandler"/>
</responseFlow>
Serialization and de-serialization directives are also defined inside of the service element. This
external definition differs from the client directives which are completed in code before the
service is invoked. For additional information about this directive see item 2 in JAVA DTS
CORE SERVICE EXAMPLES of this document.
Version: 1.0
02/17/2016
Status: Approved
Page 15
Below is an example of one of the directives:
<beanMapping languageSpecificType="java:com.datatransportstandard.referenceimplementation.serializable.DTSRequestRouting"
qname="ns2:DTSRequestRouting" xmlns:ns2="urn:org:pesc:datatransport"/>
Java Considerations - Packaging and third party utilities
The current reference implementation has one dtsreferencimpl.jar file containing all of the
service classes to be placed in the DTS webapp lib directory. The following is a list of the
minimum supporting jars that are required to have the service function. The DTS webapp lib
directory in the reference implementation has all of these files. These files can also be found in
the Apache Axis 1.1 Web Services toolkit.
saaj.jar
commons-logging.jar
commons-discovery.jar
jaxrpc.jar
wsdl4j.jar
log4j-1.2.8.jar
axis.jar
Version: 1.0
Status: Approved
February 2006
Page 16
7
Net CORE CLIENT EXAMPLES
Note: See the .Net reference implementation for full context of code snippet examples in this
section.
1. Error reporting to DTS Client Application prior to calling the service:
Since the .Net CoreClient implementation is a wrapper around the proxy class to the
web reference, validation of the elements occurs inside the submitDTS method of the
proxy. This ensures that all header element values are being evaluated correctly and
occurs prior to actually invoking the web service across the wire. The CoreClient
wrapper class to the web reference will raise the exception back to the Client Application
if any element validation fails.
Below is an example of one of the validation checks:
if (this.DTSRequestRoutingVal.sourceID.Length == 0)
{
throw new ApplicationException("DTSRequestRouting.sourceID empty");
}
2. Create container classes for header elements:
This item is described in combination with item 3.
3. Directives for serialization/de-serialization:
To accomplish interoperability with the Java platform, the .Net header elements
container classes have to be created differently for a request header versus a response
header, as well as apply specific serialization directives to the container classes.
The container classes for the header objects are added to the reference.cs file (proxy class
to Web Service) that is added to a CoreClient wrapper project either manually or by
adding a web reference. If adding a reference to a service that already exists, the
container classes created automatically must be augmented with directives as shown in
the example below. This is required for all request and response header elements. See
the DTS Specification Document for the complete WSDL and SOAP examples.
Below is an example of the different declarations of the classes for the
DTSRequestRouting and DTSResponseRouting header objects contained in the proxy
class:
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
[System.Xml.Serialization.XmlRootAttribute(ElementName="DTSRequestRouting",
Namespace="urn:org:pesc:datatransport", IsNullable=false)]
[System.Xml.Serialization.XmlInclude(typeof(DTSRequestRouting))]
public class DTSRequestRoutingElements : System.Web.Services.Protocols.SoapHeader
{
public string UUID;
Version: 1.0
02/17/2016
Status: Approved
Page 17
public string transmissionDateTime;
public string sourceID;
public string sourceIDSubCode;
public string recipientID;
public string recipientIDSubCode;
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
public class DTSRequestRouting : DTSRequestRoutingElements
{
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(DTSResponseRouting))]
[System.Xml.Serialization.XmlRootAttribute("DTSResponseRouting",
Namespace="urn:org:pesc:datatransport", IsNullable=false)]
public class DTSResponseRouting : System.Web.Services.Protocols.SoapHeader
{
public string UUID;
public string transmissionDateTime;
public string sourceID;
public string sourceIDSubCode;
public string recipientID;
public string recipientIDSubCode;
}
The actual element DTSRequestRouting that is in the SOAP is created by first creating a
DTSRequestRoutingElements class from which it inherits. The key to the
interoperability lies in how the .Net framework serializes the class when it is packaged,
so the XMLRootAttribute of “DTSRequestRouting” and the XMLInlcude specifying the
type DTSRequestRouting are critical. By creating the local variable for the container
header object in the CoreClient of type DTSRequestRoutingElements and directing the
framework to serialize of type DTSRequestRouting the SOAP that will be created will
contain the critical attribute “xsi:type=DTSRequestRouting.” The .Net framework does
not need the xsi:type attribute to properly de-serialize and access the SOAP header
objects. Therefore, all that is necessary for the DTSResponseRouting container class is a
single class with all the elements.
This is how the local variable for the header element in the CoreClient wrapper class is
declared:
private DTSCore.DTSRequestRouting dtsrequestrouting = new DTSCore.DTSRequestRouting();
And this is how the variable used by the proxy that will get assigned the CoreClient’s
object is declared:
public DTSRequestRoutingElements
DTSRequestRoutingVal;
4. DTS Request Header processing:
Below is an example of the CoreClient wrapper of the reference.cs proxy class creating
and setting a DTSRequestRouting header.
string tempDateTimeStamp = string.Empty;
dtsrequestrouting.sourceID = this.getSourceID();
dtsrequestrouting.sourceIDSubCode = this.getSourceIdSubCode();
dtsrequestrouting.recipientID = this.getRecipientID();
Version: 1.0
Status: Approved
February 2006
Page 18
dtsrequestrouting.recipientIDSubCode = this.getRecipientIDSubCode();
dtsrequestrouting.UUID = this.getSourceIdSubCode();
//format date time
tempDateTimeStamp = System.DateTime.UtcNow.ToString();
tempDateTimeStamp = tempDateTimeStamp.Remove((int)tempDateTimeStamp.Length-3,3) +
"." + System.DateTime.UtcNow.Millisecond.ToString() + "Z";
dtsrequestrouting.transmissionDateTime = tempDateTimeStamp;
//set the routing object of the service proxy
this.dtsserviceproxy.DTSRequestRoutingVal = dtsrequestrouting;
The above coding must be done for each of the header elements of the DTS Request. Fill
the local header element variable of the CoreClient with everything from the interface
variables of the CoreClient and then set the web service proxy class’s header element to
the CoreClient’s header element object.
5. Preparing the payload:
Below is an example of compressing and encoding the payload. The compression utility
class is used to compress the payload and it has additional functions for encoding as
well. Encoding is performed by the Convert.ToBase64String contained in the
system library. The compression class will also return several other types where the
encoding can be done in a second step.
//compress and encode the payload
this.Payload = Compression.CompressEncodeString(this.Payload);
6. Signing the payload:
Below is an example of signing and base64 encoding the payload and setting the
DTSRequestSignature SOAP header element.
private void FillDTSRequestSignature()
{
//this function will create the signature of the compressed and encoded payload
//using the privatekey set by the client app
//The setting of the private key and usage hasn't been fully developed in terms
//of an interface implementation. There are several aspects for the .net imp.
//that need to be resolved.
//for now- the privatekey private var will be the key name to look up and we will
//use the class built and proven-- this is an aspect that needs to be deal with
//TODO: fulfill interface implementation of setPrivateKey and usage of //IPrivateKeyHandler
#region This is what needs to be changed once decision of key repository is made
//Declarations
HashAlgorithm sha1m = new SHA1Managed();
byte[] signature;
//get cert and then RSA private key
Certificate oCert = new Certificate(this.PrivateKey);
RSACryptoServiceProvider rsaPrv = oCert.RSAPrivateFromCertificate();
#endregion
signature = rsaPrv.SignData(ae.GetBytes(this.Payload),sha1m);
//set signature in local header object
Version: 1.0
02/17/2016
Status: Approved
Page 19
this.dtsrequestsignature.value = Convert.ToBase64String(signature);
//set the object of the service proxy
this.dtsserviceproxy.DTSRequestSignatureVal = this.dtsrequestsignature;
}
7. Invoking the service:
Below is the call from the CoreClient wrapper to the submitDTS method inside the
proxy class to the web reference. This call would be inside the actual invoke() method of
the CoreClient after all the other properties of the proxy class were set as described
above.
//call DTS Service Core
this.Response = this.dtsserviceproxy.submitDTS(this.Payload);
See the reference implementation for the actual submitDTS method declaration
contained in the proxy class (reference.cs). Most of the work is done if the web reference
was added by referencing a service that already exists, though there are modifications
that must be made that match the examples above. The attributes/directives control
serialization of all the SOAP elements described above and match the objects declared in
the proxy class.
Below is what is contained inside the submitDTS method of the proxy class
(reference.cs). The catch blocks inside the proxy class will simply throw them back to
the CoreClient wrapper, see Exception Handling for further details.
public string submitDTS(
[System.Xml.Serialization.XmlElementAttribute
(Namespace="urn:org:pesc:datatransport")] string submitDTSRequest)
{
try
{
//pre validation
ValidateHeader();
object[] results = this.Invoke("submitDTS", new object[] {submitDTSRequest});
return ((string)(results[0]));
}
catch (SoapException sx)
{
throw sx;
}
catch (Exception ex2)
{
//This is where to catch network and serialization exceptions
throw ex2;
}
}
8. Exception handling and reporting from DTS Core Service:
A SOAP exception will be thrown out of the proxy class when it is actually reported
from the DTS Core Service. Otherwise it will be a normal .Net exception. The below is
the catch structure contained in the CoreClient in the invoke method.
Version: 1.0
Status: Approved
February 2006
Page 20
catch (SoapException SoapEx)
{
//this exception would actually come from the service
//this would be where the core client would reformat a SOAP
//exception into an appropriate platform specific exception
//to be thrown back to the Client ApplicationXmlQualifiedName xmlName = SoapEx.Code;
XmlNode xmlNode = SoapEx.Detail;
string faultNumber = xmlName.Name;
string faultDescription = SoapEx.Message;
StringBuilder faultDetailSB = new StringBuilder();
XmlNodeList detaillist = SoapEx.Detail.SelectNodes("string");
for (int counter = 0; counter <= detaillist.Count-1; counter++)
{
faultDetailSB.Append(detaillist.Item(counter).InnerXml + "\r\n");
}
throw new ApplicationException(faultDetailSB.toString());
}
catch (Exception Ex)
{
throw Ex;
}
By catching the SOAP exception first, all nodes can be interrogated and reformatted in
any manner to be thrown back to the Client Application as a platform exception. Any
other exception will simply go straight back to the client.
9. Response header processing:
Below is an example of how the CoreClient extracts the DTS Response header elements
from the proxy class. Because the proxy class handles all the serialization, the header
elements have already been de-serialized into native objects of the class. The Client
Application can then retrieve the values from the CoreClient using the “getters.”
private void FillResponseHeaderLocalVars()
{
this.PayloadBytes = this.dtsserviceproxy.DTSResponsePayloadBytesVal.value;
this.PayloadType = this.dtsserviceproxy.DTSResponsePayloadTypeVal.value;
this.ResponseAcknowledge = this.dtsserviceproxy.DTSResponseAcknowledgeVal.value;
//routing header elements
this.setSourceId(this.dtsserviceproxy.DTSResponseRoutingVal.sourceID);
this.setSourceIdSubCode(this.dtsserviceproxy.DTSResponseRoutingVal.sourceIDSubCode);
this.setRecipientId(this.dtsserviceproxy.DTSResponseRoutingVal.recipientID);
this.setRecipientIdSubCode(this.dtsserviceproxy.DTSResponseRoutingVal.recipientIDSubCode);
this.setUUID(this.dtsserviceproxy.DTSResponseRoutingVal.UUID);
this.transmissionDateTimeStamp = this.dtsserviceproxy.DTSResponseRoutingVal.transmissionDateTime;
this.ResponseSignature = this.dtsserviceproxy.DTSResponseSignatureVal.value;
}
10. Verifying the response signature:
Version: 1.0
02/17/2016
Status: Approved
Page 21
Below is an example of validating the response signature. This example also assumes
the public key set will be the name of the certificate. This may change based on
individual organizations’ adaptation of a key store.
private void ValidateReturnSignature(string responseSignature, string tempPayload)
{
//create hash on this side
HashAlgorithm sha = new SHA1Managed();
//create hash of base 64 encoded payload
byte[] hashedPayload = sha.ComputeHash(this.ae.GetBytes(tempPayload));
Certificate oCert = new Certificate(this.PublicKey);
RSACryptoServiceProvider rsaPub = oCert.RSAPublicFromCertificate();
//verify the hash of hash
bool bverifyhash = rsaPub.VerifyHash(hashedPayload,CryptoConfig.MapNameToOID("SHA1"),
Convert.FromBase64String(responseSignature));
if (!bverifyhash)
{
throw new ApplicationException("Return signature verification failed");
}
else
{
//it was good
}
}
11. Response processing:
Below is an example of un-compressing and decoding the payload. The compression
utility class is used to un-compress the payload and it has additional functions for
decoding as well. Decoding is performed by the Convert.FromBase64String
contained in the system library. The compression class will also return several other
types where the decoding can be done in a second step.
//set response
//this.Response was the return of the submitDTS method call.
this.Response = Compression.DecodeDecompressString(this.Response);
Version: 1.0
Status: Approved
February 2006
Page 22
8
NET CORE SERVICE EXAMPLES
1. Create container classes for header elements.
2. Directive for serialization/de-serialization:
For the .Net implementation, the Response headers have to be created specially to allow for
interoperability and the incoming Request headers can be very simple. The creation of the
container classes for the service is the reverse of the creation of the container classes for the client.
The container classes are created as part of the asmx file of the web service. Below is an
example of the declaration and attributes/directives of the DTSResponseRouting and
DTSRequestRouting header elements.
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
[System.Xml.Serialization.XmlRootAttribute(ElementName="DTSResponseRouting",
Namespace="urn:org:pesc:datatransport", IsNullable=false)]
[System.Xml.Serialization.XmlInclude(typeof(DTSResponseRouting))]
public class DTSResponseRoutingElements : System.Web.Services.Protocols.SoapHeader
{
public string UUID;
public string transmissionDateTime;
public string sourceID;
public string sourceIDSubCode;
public string recipientID;
public string recipientIDSubCode;
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
public class DTSResponseRouting : DTSResponseRoutingElements
{
}
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:org:pesc:datatransport")]
[System.Xml.Serialization.XmlRootAttribute(ElementName="DTSRequestRouting",
Namespace="urn:org:pesc:datatransport", IsNullable=false)]
[System.Xml.Serialization.XmlInclude(typeof(DTSRequestRouting))]
public class DTSRequestRouting : System.Web.Services.Protocols.SoapHeader
{
public string UUID;
public string transmissionDateTime;
public string sourceID;
public string sourceIDSubCode;
public string recipientID;
public string recipientIDSubCode;
}
The creation of these container classes in this manner follows the same logic as the
creation of the Client container classes. However, because the response header is the
object that needs to be serialized with the xsi:type attribute, it is the one that inherits
from the “elements” class. .Net does not need the xsi:type attribute and the Request
elements will be de-serialized and accessed correctly.
3. Request Header processing:
Version: 1.0
02/17/2016
Status: Approved
Page 23
Below is an example of validating the DTSRequestRouting header object. The SOAP
fault is raised by use of an internal class. All header elements have similar routines for
validation.
private void ValidateRouting()
{
DTSSoapException exception = new DTSSoapException();
DTSRequestRouting routing = this.DTSRequestRoutingVal;
if (routing == null)
{
exception.AddException("ROUTING_TAG_MISSING");
}
else
{
if (routing.sourceID == null)
{
exception.AddException("SOURCEID_TAG_MISSING");
}
else if (routing.sourceID.Length == 0)
{
exception.AddException("SOURCEID_MISSING");
}
if (routing.recipientID == null)
{
exception.AddException("RECIPIENTID_TAG_MISSING");
}
else if (routing.recipientID.Length == 0)
{
exception.AddException("RECIPIENTID_MISSING");
}
if (routing.UUID == null)
{
exception.AddException("UUID_TAG_MISSING");
}
else if (DTSRequestRoutingVal.UUID.Length == 0)
{
exception.AddException("UUID_MISSING");
}
if (routing.transmissionDateTime == null)
{
exception.AddException("TRANSDT_TAG_MISSING");
}
else if (DTSRequestRoutingVal.transmissionDateTime.Length == 0)
{
exception.AddException("TRANSDT_MISSING");
}
else
{
try
{
DateTime testDateTime = DateTime.Parse(
DTSRequestRoutingVal.transmissionDateTime);
}
catch(FormatException)
{
//exception.AddException("TRANSDT_ILLEGAL");
}
}
}
exception.ThrowIfError();
}
Version: 1.0
Status: Approved
February 2006
Page 24
Below is the method in the DTSException internal class that adds the information to the
structure that will be used to create the appropriate SOAP Fault.
internal void AddException(string key)
{
//Find the excetion info for supplied key
DTSValidation info = null;
for (int index = 0; index < exceptionInfo.Length; ++index)
{
if (exceptionInfo[index].Key == key)
{
info = exceptionInfo[index];
break;
}
}
if (info != null)
{
if (m_code == null)
{
m_message = info.Message;
m_code = info.Code;
XmlDocument doc = new XmlDocument();
XmlNode node = doc.CreateNode(XmlNodeType.Element,
SoapException.DetailElementName.Name,
SoapException.DetailElementName.Namespace);
XmlNode detailsChild = doc.CreateNode(XmlNodeType.Element, "string","");
detailsChild.InnerText = info.Detail;
node.AppendChild(detailsChild);
string xml = node.OuterXml;
m_details = node;
}
else
{
XmlQualifiedName newCode = info.Code;
if (newCode != m_code)
{
//throw application error
}
else
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(m_details.OuterXml);
XmlNode root = doc.DocumentElement;
XmlNode child = root.FirstChild;
//<string>
while (true)
{
XmlNode nextChild = child.NextSibling;
if (nextChild != null)
{
string innerTest = nextChild.OuterXml;
child = nextChild;
}
else
{
break;
}
}
XmlNode detailMessage = doc.CreateNode(XmlNodeType.Element, "string","");
detailMessage.InnerText = info.Detail;
root.InsertAfter(detailMessage, child);
string xml = root.OuterXml;
m_details = root;
}
Version: 1.0
02/17/2016
Status: Approved
Page 25
}
}
else
{
//throw an application error
//
key not found
}
}
Below is an excerpt of the array that contains the key information that actually fills the
information of the SOAP fault being created for the RequestRouting validation.
DTSValidation[] exceptionInfo =
{
//Routing information validations
new DTSValidation("ROUTING_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Information is not set in the SOAP Packet"),
new DTSValidation("SOURCEID_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing SourceId Element was not found."),
new DTSValidation("SOURCEID_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Source Id value is missing."),
new DTSValidation("SOURCE_CODE_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing sourceIDSubCode Element was not found."),
new DTSValidation("SOURCE_CODE_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing sourceIDSubCode value is missing."),
new DTSValidation("RECIPIENTID_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Recipient Id Element was not found."),
new DTSValidation("RECIPIENTID_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Recipient Id value is missing."),
new DTSValidation("UUID_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing UUID Element was not found."),
new DTSValidation("UUID_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing UUID value is missing."),
new DTSValidation("TRANSDT_TAG_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing TransmissionDateTime Element was not found."),
new DTSValidation("TRANSDT_MISSING", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Transmission Date Time value is missing."),
new DTSValidation("TRANSDT_ILLEGAL", "DTS.HEADER.001",
"DTS SOAP Header Error with DTS Routing Information",
"DTS Request Routing Transmission Date Time value is not in
ccyy-mm-ddTHH:mm:ss.ffZ format."),
.
.
.
}
4. Instantiate the appropriate DTS Service Application:
The DTS CoreService instantiates a DTS ServiceApplication that must implement the
IDTSServiceApplication interface. The actual implementation and instantiating of this
object can be different depending on many different .Net configuration options. For the
Version: 1.0
Status: Approved
February 2006
Page 26
reference implementation this object resides in the bin directory of the CoreService that
is deployed. After adding a reference to the object the following code instantiates this
object.
using ServiceApplication;
//create instance of ServiceApplication
private ServiceApplication.DTSReferenceServiceApplication dtsAppl =
new DTSReferenceServiceApplication();
After the validation of the header objects is complete the values can be set into the
ServiceApplication. The following code does it.
//routing objects
dtsAppl.setSourceId(this.DTSRequestRoutingVal.sourceID);
dtsAppl.setSourceIdSubCode(this.DTSRequestRoutingVal.sourceIDSubCode);
dtsAppl.setRecipientId(this.DTSRequestRoutingVal.recipientID);
dtsAppl.setRecipientIdSubCode(this.DTSRequestRoutingVal.recipientIDSubCode);
dtsAppl.setUUID(this.DTSRequestRoutingVal.UUID);
dtsAppl.setTransmissionDateTime(this.DTSRequestRoutingVal.transmissionDateTime);
//others except payload- need to do more work for that one
dtsAppl.setServiceExpectation(this.DTSRequestServiceExpectationVal.value);
dtsAppl.setPayloadType(this.DTSRequestPayloadTypeVal.value);
5. Verifying the request signature:
The DTS Service Application instantiated in step 4 is required to implement the
“getPublicKey” method that returns a RSACryptoServiceProvider object. After the
CoreService sets the values in the ServiceApplication for the header elements, the DTS
Core Service makes a call to the method to retrieve the public key to verify the request
signature. If there is a problem retrieving the key the ServiceApplication should raise an
ApplicationException to the CoreService which will then be reconfigured to a SOAP
Fault to be sent back to the CoreClient. If the signature does not verify, the DTS Core
Service should throw a SOAP Fault back to the DTS Core Client according to the
specification. The following code example shows how the DTS Core Service would
accomplish this.
private void VerifySignature(string Signature, string tempPayload)
{
DTSSoapException exception = new DTSSoapException();
ASCIIEncoding ae = new ASCIIEncoding();
//create hash on this side
HashAlgorithm sha = new SHA1Managed();
//create hash of base 64 encoded payload
byte[] hashedPayload = sha.ComputeHash(ae.GetBytes(tempPayload));
try
{
//the service application will raise an error if there is a problem
RSACryptoServiceProvider rsaPub = dtsAppl.getPublicKey();
//verify the hash of hash
bool bverifyhash = rsaPub.VerifyHash(hashedPayload,
CryptoConfig.MapNameToOID("SHA1"),
Version: 1.0
02/17/2016
Status: Approved
Page 27
Convert.FromBase64String(Signature));
if (!bverifyhash)
{
exception.AddException("SIGNATURE_NOT_VALID");
}
}
catch (Exception ex)
{
exception.AddException("SOURCE_PUBLIC_KEY_NOT_FOUND");
}
exception.ThrowIfError();
}
6. Payload processing:
The following code shows how the DTS CoreService validates the payload which
includes decoding and uncompressing it. Then it sets the payload into the
ServiceApplication.
//validate the payload- decodes and uncompresses
payload = ValidatePayload(submitDTSRequest);
private string ValidatePayload(string payload)
{
DTSSoapException exception = new DTSSoapException();
string PayloadType = this.DTSRequestPayloadTypeVal.value;
string DecodedUncompressedPayload = string.Empty;
ASCIIEncoding ae = new ASCIIEncoding();
try
{
Convert.FromBase64String(payload);
}
catch
{
//with only one statement we know its decoding
exception.AddException("PAYLOAD_DECODE_ERROR");
}
try
{
DecodedUncompressedPayload = Compression.DecodeDecompressString(payload);
}
catch
{
exception.AddException("PAYLOAD_DECOMPRESS_ERROR");
}
if (DecodedUncompressedPayload.Length == 0 && PayloadType != "retrieve")
{
exception.AddException("PAYLOAD_MISSING");
}
exception.ThrowIfError();
return DecodedUncompressedPayload;
}
7. Invoking a DTS Service Application:
Version: 1.0
Status: Approved
February 2006
Page 28
Once all elements required by DTS are populated in the ServiceApplication the invoke
method can be called.
//set payload to service appl
dtsAppl.setPayload(payload);
//Invoke service appl
dtsAppl.invoke();
8. DTS Service Application exception handling:
Below is the method in the DTSException internal class that will create and throw the
SOAP Fault. This method is called after the validation of header objects.
internal void ThrowIfError()
{
if (m_code != null)
{
SoapException exception = new SoapException(m_message, m_code, m_actor, m_details);
throw exception;
}
}
The catch blocks below are in the submitDTS method of the service. Catching the
SoapException thrown by the code above first allows the already formatted SOAP Fault
to be thrown back to the CoreClient. Catching any other exceptions independently gives
the ability to reformat the internal error to the DTS.APPLICATION fault.
catch (SoapException s)
{
//throw s;
throw new SoapException(s.Message ,s.Code ,s.Actor,s.Detail);
}
catch(Exception e)
{
string message = e.ToString();
XmlQualifiedName internalErrorName = new XmlQualifiedName("Internal Error",
"urn:org:pesc:datatransport");
SoapException internalErrorException = new SoapException(message, internalErrorName);
throw internalErrorException;
}
9. Preparing the response:
The code below compresses and encodes the response that will be the return of the
submitDTS method.
//Compress the return
tempRtn = Compression.CompressEncodeString(dtsAppl.getResponse);
Version: 1.0
02/17/2016
Status: Approved
Page 29
The above line is not actually found in the reference implementation since it is doing
things differently. The tempRtn variable is initially populated in the CoreClient with the
length of the compressed payload. Then the actual response from the
ServiceApplication is concatenated to it as well as the time it took to verify the incoming
signature. All of this can be seen in the reference implementation, though in a regular
application the CoreService would simply prepare what the ServiceApplication
provided.
10. Signing the response:
Below is an example of how the DTS Core Service signs the response. The private key
for signing is obtained through the interface call to the DTS Service Application
“getPrivateKey.”
DTSResponseSignature returnSignature = new DTSResponseSignature();
//create hash algorithm object
HashAlgorithm sha1m = new SHA1Managed();
//compute the hash
byte[] hash = sha1m.ComputeHash(ae.GetBytes(tempRtn));
byte[] signature;
//hash created above; needed for Java Interop
//now need to build the RSA object
//build rsa provider with private rsa key
RSACryptoServiceProvider rsaPrv = dtsAppl.getPrivateKey();
signature = rsaPrv.SignData(ae.GetBytes(tempRtn),sha1m);
//set signature header element = to signed hash string
returnSignature.value = Convert.ToBase64String(signature);
//set the object
this.DTSResponseSignatureVal = returnSignature;
11. Preparing the Response Headers:
With the invoke method being called the full response can be prepared. The following
code populates the response header objects from the “get” interfaces of the
ServiceApplication.
//assign values to the local return headers
returnRouting.sourceID = dtsAppl.getSourceID();
returnRouting.sourceIDSubCode = dtsAppl.getSourceIdSubCode();
returnRouting.recipientID = dtsAppl.getRecipientID();
returnRouting.recipientIDSubCode = dtsAppl.getRecipientIDSubCode();
returnRouting.UUID = dtsAppl.getUUID();
//we set the transmission date time instead of getting it from dtsAppl
returnRouting.transmissionDateTime = System.DateTime.UtcNow.ToString();
returnRouting.transmissionDateTime = returnRouting.transmissionDateTime.Remove(
(int)returnRouting.transmissionDateTime.Length-3,3) + "." +
System.DateTime.UtcNow.Millisecond.ToString();
//simples
returnPayloadType.value = dtsAppl.getPayloadType();
returnAcknowledge.value = dtsAppl.getResponseAcknowledge();
returnPayloadBytes.value = tempRtn.Length.ToString();
//Set the objects to the public variables to be serialized and returned
this.DTSResponseRoutingVal = returnRouting;
this.DTSResponseAcknowledgeVal = returnAcknowledge;
Version: 1.0
Status: Approved
February 2006
Page 30
this.DTSResponsePayloadTypeVal = returnPayloadType;
this.DTSResponsePayloadBytesVal = returnPayloadBytes;
Version: 1.0
02/17/2016
Status: Approved
Page 31
9
JAVA DTS CORE CLIENT EXAMPLES
1. Error reporting to DTS Client Application prior to calling the service:
if (this.getSourceId.length() == 0)
{
Exception e = new Exception(“Length of Source Id = 0”);
throw e;
}
Subsequent checks of all of the remaining required elements should be done in
succession.
2. Create container classes for header elements:
Below is an example of a DTSRequestRouting container class. The private elements of
the class match up with the type definition for the DTSRequestRouting element defined
in the WSDL.
package com.datatransportstandard.referenceimplementation.serializable;
import java.io.Serializable;
public class DTSRequestRouting implements Serializable
{
private String sourceID
private String sourceIDSubCode
private String recipientID
private String recipientIDSubCode
private String uuid
private String transmissionDateTime
= null;
= null;
= null;
= null;
= null;
= null;
public String getSourceID()
{
return sourceID;
}
public void setSourceID(String newSourceID)
{
sourceID = newSourceID;
}
public String getSourceIDSubCode()
{
return sourceIDSubCode;
}
public void setSourceIDSubCode(String newSourceIDSubCode)
{
sourceIDSubCode = newSourceIDSubCode;
}
public String getRecipientID()
{
return recipientID;
}
public void setRecipientID(String newRecipientID)
Version: 1.0
Status: Approved
February 2006
Page 32
{
recipientID = newRecipientID;
}
public String getRecipientIDSubCode()
{
return recipientIDSubCode;
}
public void setRecipientIDSubCode(String newRecipientIDSubCode)
{
recipientIDSubCode = newRecipientIDSubCode;
}
public String getUUID()
{
return uuid;
}
public void setUUID(String newUUID)
{
uuid = newUUID;
}
public String getTransmissionDateTime()
{
return transmissionDateTime;
}
public void setTransmissionDateTime(String newTransDateTime)
{
transmissionDateTime = newTransDateTime;
}
}
Container classes need to be created for all request and response header elements. The
following is a complete list of header container classes that need to be created. They all
have similar structures. You should reference the WSDL or the SOAP examples for their
exact structure.
 DTSRequestRouting
 DTSRequestPayloadType
 DTSRequestServiceExpectation
 DTSRequestSignature
 DTSRequestPayloadBytes
 DTSResponseRouting
 DTSReponsePayloadType
 DTSResponseAcknowledge
 DTSResponseSignature
 DTSResponsePayloadBytes
Version: 1.0
02/17/2016
Status: Approved
Page 33
3. Directives for serialization/de-serialization:
Below is an example of calling the Java directive to allow the Apache Axis engine to
serialize and de-serialize the DTSRequestRouting object.
QName qn = new QName(“urn:org:pesc:datatransport”,"DTSRequestRouting");
call.registerTypeMapping(DTSRequestRouting.class,
qn,
new BeanSerializerFactory(DTSRequestRouting.class, qn),
new BeanDeserializerFactory(DTSRequestRouting.class, qn));
The QName object defines the qualified name attribute and the namespace attribute to
be assigned to the SOAP element. Namespace = urn:org:pesc:datatransport Qualified
Name = DTSRequestRouting.
It is very important that these two attributes are set correctly and match what is created
on the header element. The DTS Core Service is going to be extracting the header
elements based on the same QName attributes. If they do not match, the DTS Core
Service will not be able to read the header even if the header is present in the SOAP.
The DTS Core Service will raise a SOAP fault if it cannot find the header element.
The above directive needs to be called for each DTS Header element based on the
container classes you should have created in step 2.
4. DTS Request Header processing:
Below is an example of the creation, filling, and setting of the DTSRequestRouting
header element into the SOAP.
DTSRequestRouting requestrouting = new DTSRequestRouting();
requestrouting.setSourceID(this.getSourceId());
requestrouting.setSourceIDSubCode(this.getSourceIdSubCode());
requestrouting.setRecipientID(this.getRecipientId());
requestrouting.setRecipientIDSubCode(this.getRecipientIdSubCode);
requestrouting.setUUID(this.getUUID());
requestrouting.setTransmissionDateTime(this.getTransmissionDateTime());
SOAPHeaderElement headerElement = new SOAPHeaderElement(“urn:org:pesc:datatransport”, "DTSRequestRouting");
headerElement.setObjectValue(requestrouting);
call.addHeader(headerElement);
Note: The namespace and qualified name of the SOAPHeaderElement instantiation
match the namespace and qualified name called in the QName instantiation of the
serialization/de-serialization directive.
The above coding needs to be completed for each header element of the DTS request.
Version: 1.0
Status: Approved
February 2006
Page 34
5. Preparing the payload:
Below is an example of preparing the payload. The zlib utility class is used to compress
the payload. The encoding is performed from the base64 object in the
org.apache.axis.encoding package.
private String preparePayload() throws IOException
{
// Zlib Compress the payload
byte[] compressedpayload = Zlib.compress(payload.getBytes());
logString += ",ClientCompressed," + compressedpayload.length;
// Base64 encode the payload
return Base64.encode(compressedpayload);
}
6. Signing the payload
Below is an example of how to sign and base64 encode the payload and put the results
into the DTSRequestSignature SOAP Header.
private String signThePayload(String encodedpayload) throws CertificateException,
IOException,
InvalidKeyException,
KeyStoreException,
NoSuchAlgorithmException,
UnrecoverableKeyException,
SignatureException
{
start = new Date();
keyStore = KeyStore.getInstance(dtsprops.getPrivateKeystoreType());
keyStoreStream = new FileInputStream(dtsprops.getPrivateKeystore());
keyStore.load(keyStoreStream, dtsprops.getPrivateKeystorePassword().toCharArray());
Signature sign = Signature.getInstance(dtsprops.getPrivateKeystoreAlgo());
sign.initSign((PrivateKey) keyStore.getKey(dtsprops.getPrivateKeyAlias(),
dtsprops.getPrivateKeyPassword().toCharArray()));
sign.update(encodedpayload.getBytes());
byte[] signed = sign.sign();
String encodedSigned = Base64.encode(signed);
end = new Date();
logString += ",ClientSignatureMS," + (end.getTime() - start.getTime());
return encodedSigned;
}
DTSRequestSignature requestsignature = new DTSRequestSignature();
requestsignature.setValue(encodedSigned);
SOAPHeaderElement headerElement = new SOAPHeaderElement(“urn:org:pesc:datatransport”,
"DTSRequestSignature");
headerElement.setObjectValue(requestsignature);
call.addHeader(headerElement);
Version: 1.0
02/17/2016
Status: Approved
Page 35
7. Invoking the service:
For the DTS Core client to function properly some preliminary set-up is required prior to
invoking a DTS Core Service. The operations object tells the call how to format the
SOAP. Namespace and qualified names should match what is defined in the WSDL.
private void prepareOperation()
{
operations = new org.apache.axis.description.OperationDesc[1];
OperationDesc oper = new org.apache.axis.description.OperationDesc();
// Set the logical name of the operation
oper.setName("submitDTS");
// Payload SOAP Defintion
oper.addParameter(
new javax.xml.namespace.QName(“urn:org:pesc:datatransport”, "submitDTSRequest"),
new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"),
java.lang.String.class, org.apache.axis.description.ParameterDesc.IN, false, false);
// Return type from Service
oper.setReturnType(
new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"));
// Class associated with the return type
oper.setReturnClass(java.lang.String.class);
// Return type SOAP Definiton
oper.setReturnQName(
new javax.xml.namespace.QName(“urn:org:pesc:datatransport”, submitDTSResponse"));
// SOAP Style
oper.setStyle(org.apache.axis.enum.Style.DOCUMENT);
// Encoding Style
oper.setUse(org.apache.axis.enum.Use.LITERAL);
operations[0] = oper;
}
private void prepareCall() throws ServiceException, MalformedURLException
{
service = new Service();
call = (Call) service.createCall();
// Set the operation object form prepareOperation method
call.setOperation(operations[0]);
// Set the target URL
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
// Set the operation name to be called
call.setOperationName(new QName("", "submitDTS"));
}
try
{
String callresp = (String) call.invoke( new String[] { encodedpayload } );
}
// See the exception handling catch block in the next section
8. Exception handling and reporting returned from DTS Core Service:
Below is an example of the catch portion of the try/catch block of the DTS Core Client
that surrounds the invoke to the DTS Core Service. In this case, an AxisFault is a
specialized SOAP fault. The reference implementation uses the System.out object to
display to contents of the AxisFault and then the exception is thrown to the DTS Client
Application.
catch (Exception e)
{
if (e instanceof AxisFault)
Version: 1.0
Status: Approved
February 2006
Page 36
{
AxisFault fault = (AxisFault) e;
// Insert Logging Here
System.out.println(fault.getFaultCode().getLocalPart());
System.out.println(fault.getFaultString());
Element[] faultdetails = fault.getFaultDetails();
for(int i = 0; i < faultdetails.length; i++)
{
Element detail = faultdetails[i];
// More logging here
System.out.println(detail.getFirstChild().getNodeValue());
}
}
// Throw the fault to the DTS Client Application to deal with
throw e;
}
9. DTS Response header processing:
Below is an example of how the DTS Core Client extracts the DTS Response Routing
Header element out of the SOAP packet returned by the DTS Core Service. The
namespace and qualified name work exactly the same as the DTS Request header
elements and must match exactly to the DTS Core Service creation names.
SOAPEnvelope responseEnv =
call.getMessageContext().getResponseMessage().getSOAPEnvelope();
SOAPHeaderElement headerElement = responseEnv.getHeaderByName(
“urn:org:pesc:datatransport”,"DTSResponseRouting");
if(headerElement != null)
{
DTSResponseRouting responserouting = (DTSResponseRouting)
headerElement.getObjectValue();
if(responserouting.getSourceId().length() == 0)
{
Exception e = new Exception(“Length of Source Id = 0”);
throw e;
}
else
sourceid = responserouting.getSourceId();
if(responserouting.getSourceIdSubCode().length() == 0)
{
Exception e = new Exception(“Length of Source Id Sub Code = 0”);
throw e;
}
else
sourceidsubcode = responserouting.getSourceIdSubCode();
…
}
else
{
Exception e = new Exception(“DTS Response Routing Element not Found.”);
throw e;
}
Version: 1.0
02/17/2016
Status: Approved
Page 37
10. Verifying the response signature:
SOAPHeaderElement headerElement =
responseEnv.getHeaderByName(“urn:org:pesc:datatransport”,"DTSResponseSignature");
if(headerElement != null)
{
responsesignature = (DTSResponseSignature) headerElement.getObjectValue();
if(responsesignature.getValue().length() == 0)
{
Exception e = new Exception(“Length of response signature = 0”);
throw e;
}
}
else
{
Exception e = new Exception(“Response Signature Element missing.”);
throw e;
}
// Hash is the encoded compressed response passed to this function
private boolean verifyResponseSignature(String hash) throws
KeyStoreException,
NoSuchAlgorithmException,
CertificateException,
IOException,
InvalidKeyException,
SignatureException
{
keyStore = KeyStore.getInstance(dtsprops.getPublicKeystoreType());
keyStoreStream = new FileInputStream(dtsprops.getPublicKeystore());
keyStore.load(keyStoreStream, dtsprops.getPublicKeystorePassword().toCharArray());
X509Certificate cert = (X509Certificate) keyStore.getCertificate(dtsprops.getPublicKeyAlias());
Signature verify = Signature.getInstance(dtsprops.getPublicKeystoreAlgo());
byte[] signature = Base64.decode(responsesignature.getValue());
verify.initVerify(cert.getPublicKey());
verify.update(hash.getBytes());
boolean verified = verify.verify(signature);
return verified
}
Version: 1.0
Status: Approved
February 2006
Page 38
11. Response Processing:
Below is an example of how the DTS Core Client decodes and un-compresses the
response from a DTS Core Service. The zlib utility class is used to un-compress the
payload. The decoding is performed from the base64 object in the
org.apache.axis.encoding package.
private String processResponse(String resp) throws IOException
{
start = new Date();
logString += ",ReturnTime," + sdf.format(start);
// Base64 decode the response
byte[] compressedresponse = Base64.decode(resp);
// Uncompress the response
byte[] uncompressedresponse = Zlib.decompress(compressedresponse);
// Convert the response to string
String response = new String(uncompressedresponse,"UTF-8");
logString += "," + response;
return response;
}
Version: 1.0
02/17/2016
Status: Approved
Page 39
10 JAVA DTS CORE SERVICE EXAMPLES
1. Create container classes for header elements:
For the Java implementation, the container classes created for the DTS Core Client can
be used with the DTS Core Service. See the examples in the JAVA DTS CORE CLIENT
EXAMPLES section for creating the DTS container classes.
2. Directives for serialization/de-serialization
The Java service directives for handling serialization and de-serialization are defined in
the WSDD file for the service. Each header object requiring serialization and deserialization needs to have a <beanMapping> element definition in the WSDD file. It’s very
important that the qname and xmlns attributes match up to how the SOAP Header
element is created so the header element can be found in the SOAP and then be
serialized and de-serialized into something usable by the service. See the SERVICE
DEPLOYMENT SECTION of this document for more information on the WSDD file.
<beanMapping
languageSpecificType="java:com.datatransportstandard.referenceimplementation.serializable.DTSRequestRouting"
qname="ns2:DTSRequestRouting" xmlns:ns2="urn:org:pesc:datatransport"/>
3. Request Header processing:
Below is an example of one of the checks the RequestHeaderHandler object does.
AxisFault fault = new AxisFault();
headerElement = requestEnv.getHeaderByName(IDTSConstants.DTS_DEFAULT_NAMESPACE,"DTSRequestRouting");
if(headerElement != null)
{
requestrouting = (DTSRequestRouting) headerElement.getObjectValue();
if(requestrouting.getSourceID() != null)
{
if(requestrouting.getSourceID().length() == 0)
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultString("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Source Id value is missing.");
}
else
ctx.setProperty("SourceID", requestrouting.getSourceID());
}
else
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultString("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Source Id value is missing.");
}
ctx.setProperty("SourceIDSubCode", requestrouting.getSourceIDSubCode());
if(requestrouting.getRecipientID() != null)
{
if(requestrouting.getRecipientID().length() == 0)
{
fault.setFaultCodeAsString("DTS.HEADER.001");
Version: 1.0
Status: Approved
February 2006
Page 40
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Recipient Id value is missing.");
}
else
ctx.setProperty("RecipientID", requestrouting.getRecipientID());
}
else
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Recipient Id value is missing.");
}
ctx.setProperty("RecipientIDSubCode", requestrouting.getRecipientIDSubCode());
if(requestrouting.getUUID() != null)
{
if(requestrouting.getUUID().length() == 0)
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing UUID value is missing.");
}
else
ctx.setProperty("UUID", requestrouting.getUUID());
}
else
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing UUID value is missing.");
}
if(requestrouting.getTransmissionDateTime() != null)
{
if(requestrouting.getTransmissionDateTime().length() == 0)
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Transmission Date Time value is
missing.");
}
else
ctx.setProperty("TransmissionDateTime", requestrouting.getTransmissionDateTime());
}
else
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Transmission Date Time value is missing.");
}
}
else
{
fault.setFaultCodeAsString("DTS.HEADER.001");
fault.setFaultReason("DTS SOAP Header Error with DTS Routing Information");
fault.addFaultDetailString("DTS Request Routing Information is not set in the SOAP Packet");
}
if(fault.getFaultReason().length() > 0)
throw fault;
4. Instantiate the appropriate DTS Service Application:
Version: 1.0
02/17/2016
Status: Approved
Page 41
The DTS Core Service gets that class name to instantiate from a User Defined property
in the server-config.wsdd file. The getProperty method on the MessageContext object is
used to obtain the value. Standard Java calls are made to instantiate the object.
Note: Any class name referenced in the user defined property MUST implement the
IDTSServiceApplication interface.
Example from server-config.wsdd:
<parameter name="DTSServiceApplication"
value="com.datatransportstandard.referenceimplementation.service.DTSServiceApplication"/>
Code to instantiate the DTS Service Application:
String dtsserviceapplication = (String) ctx.getProperty("DTSServiceApplication");
Class applClass = Class.forName(dtsserviceapplication);
IDTSServiceApplication dtsappl = (IDTSServiceApplication) applClass.newInstance();
5. Verifying the request signature:
The DTS Service Application instantiated in step 4 is required to implement the
“getPublicKeyHandler” method that returns an object that meets the
IPublicKeyHandler interface. The DTS Core Service needs to make a call to get a
reference to this object and then call the “getPublicKey” method to obtain the public
key to verify the request signature. If the signature does not verify, the DTS Core
Service should throw a SOAP Fault back to the DTS Core Client according to the
specification. The following code example shows how the DTS Core Service would
accomplish this.
Signature verify = Signature.getInstance(IDTSConstants.PUBLIC_KEYSTORE_ALGO)
byte[] signature = Base64.decode(requestSignature);
verify.initVerify(dtsappl.getPublicKeyHandler().getPublicKey((String) ctx.getProperty("SourceID"), (String)
ctx.getProperty("SourceIDSubCode")));
verify.update(encodedpayload.getBytes());
boolean verified = verify.verify(signature);
if(!verified)
{
AxisFault fault = new AxisFault();
fault.setFaultCodeAsString("DTS.SECURITY.002");
fault.setFaultReason("DTS Security Error");
fault.addFaultDetailString("Could not validate digital signature");
throw fault;
}
6. Payload processing:
The following code show how the DTS Core Service base64 decodes and zlib
uncompresses the payload and sets the result on the DTS Service Application.
try
{
Version: 1.0
Status: Approved
February 2006
Page 42
compressedpayload = Base64.decode(encodedpayload);
uncompressedpayload = Zlib.decompress(compressedpayload);
payload = new String(uncompressedpayload,"UTF-8");
}
catch (IOException e)
{
AxisFault fault = new AxisFault();
fault.setFaultCodeAsString("DTS.PAYLOAD.003");
fault.setFaultReason("DTS Payload Error");
fault.addFaultDetailString("DTS Payload uncompress error");
throw fault;
}
catch (Exception e)
{
AxisFault fault = new AxisFault();
fault.setFaultCodeAsString("DTS.PAYLOAD.003");
fault.setFaultReason("DTS Payload Error");
fault.addFaultDetailString("DTS Payload decoding error");
throw fault;
}
7. Invoking a DTS Service Application:
dtsappl.setSourceId((String) ctx.getProperty("SourceID"));
dtsappl.setSourceIdSubCode((String) ctx.getProperty("SourceIDSubCode"));
dtsappl.setRecipientId((String) ctx.getProperty("RecipientID"));
dtsappl.setRecipientIdSubCode((String) ctx.getProperty("RecipientIDSubCode"));
dtsappl.setUUID((String) ctx.getProperty("UUID"));
dtsappl.setTransmissionDateTime((String) ctx.getProperty("TransmissionDateTime"));
dtsappl.setPayloadType((String) ctx.getProperty("RequestPayloadType"));
dtsappl.setServiceExpectation((String) ctx.getProperty("RequestServiceExpectation"));
dtsappl.setPayload(payload);
dtsappl.invoke();
8. DTS Service Application exception handling:
Below is an example of the DTS Core Service catching an exception thrown by the DTS
Service Application and reformatting it in a SOAP fault as per the specification to be
thrown to the DTS Core Client.
catch (Exception e)
{
AxisFault fault = new AxisFault();
fault.setFaultCodeAsString("DTS.APPLICATION");
fault.setFaultReason("DTS Service Application Error ");
fault.addFaultDetailString(e.getMessage());
throw fault;
}
9. Preparing the response:
Below is an example of the DTS Core Client zlib compressing and base64 Encoding the
response in preparation for return to the DTS Core Client.
compressedresponse = Zlib.compress(response.getBytes());
encodedresponse = Base64.encode(compressedresponse);
The encoded response is what gets returned.
Version: 1.0
02/17/2016
Status: Approved
Page 43
10. Signing the response:
Below is an example of how the DTS Core Service signs the response. The private key
for signing is obtained through the interface call to the DTS Service Application
“getPrivateKey.”
Signature sign = Signature.getInstance(IDTSConstants.PRIVATE_KEYSTORE_ALGO);
sign.initSign(dtsappl.getPrivateKey());
sign.update(encodedresponse.getBytes());
byte[] signed = sign.sign();
String encodedSigned = Base64.encode(signed);
ctx.setProperty("EncodedSignature", encodedSigned);
11. Preparing the Response Headers:
The DTS Response Headers are prepared the ResponseHeaderHandler object. This
object has been placed in the response flow of the server-config.wsdd. The DTS Core
Service communicates the values to be placed in the headers through the setProperty
method on the MessageContext object. The ResponseHeaderHandler then reads the
values and creates the appropriate header objects to be put in the SOAP. Below are
examples of the DTS Core Service setting the values and the ResponseHeaderHandler
creating a SOAP Header to be sent back to the DTS Core Client.
// Inside DTS Core Service
ctx.setProperty("SourceID", dtsappl.getSourceId());
ctx.setProperty("SourceIDSubCode", dtsappl.getSourceIdSubCode());
ctx.setProperty("RecipientID", dtsappl.getRecipientId());
ctx.setProperty("RecipientIDSubCode", dtsappl.getRecipientIdSubCode());
ctx.setProperty("UUID", dtsappl.getUUID());
ctx.setProperty("TransmissionDateTime", "Some Trans Date Time From Service");
ctx.setProperty("ResponsePayloadType", dtsappl.getResponseType());
ctx.setProperty("ResponseAcknowledge", dtsappl.getResponseAcknowledge());
ctx.setProperty("ResponsePayloadBytes", (new Integer(response.length()).toString()));
// Inside ResponseHeaderHandler
DTSResponseRouting responserouting = new DTSResponseRouting();
responserouting.setSourceID((String) ctx.getProperty("SourceID"));
responserouting.setSourceIDSubCode((String) ctx.getProperty("SourceIDSubCode"));
responserouting.setRecipientID((String) ctx.getProperty("RecipientID"));
responserouting.setRecipientIDSubCode((String) ctx.getProperty("RecipientIDSubCode"));
responserouting.setUUID((String) ctx.getProperty("UUID"));
responserouting.setTransmissionDateTime((String) ctx.getProperty("TransmissionDateTime"));
headerElement = new
SOAPHeaderElement(IDTSConstants.DTS_DEFAULT_NAMESPACE,"DTSResponseRouting");
headerElement.setObjectValue(responserouting);
responseEnv.addHeader(headerElement);
Note: The namespace and qualified name in the SOAPHeaderElement constructor
needed to match exactly what the client is expecting.
Version: 1.0
Status: Approved
February 2006
Page 44
Revision History
DATE
SECTION/
PAGE
DESCRIPTION
5/24/05
Whole
Document
Initial Version
10/18/05
Whole
Document
Format, grammar, and style review
and changes.
10/21/05
Code
Samples
Font and indentation
Whole
Document
Header/Footer updates
02/06/06
Version: 1.0
REQUESTED BY
MADE BY
Mark Malinoski
Nathan Chitty
Gary Sandler
Laura
Damkoehler
Mark Malinoski
Nathan Chitty
02/17/2016
Kim Shiflette
Status: Approved
Kim Shiflette
Page 45
DATE
SECTION/
PAGE
Version: 1.0
Status: Approved
DESCRIPTION
REQUESTED BY
MADE BY
February 2006
Page 46