Microsoft SOAP Toolkit Overview
Microsoft tried to build a stable and powerful runtime environment for developers to use SOAP in their applications starting with the SOAP Toolkit Version 1 in May 2000. Naturally, this is one of the fundamental goals for a developer of distributed applications; however, the SOAP mechanisms and low-level features should be hidden from the business logic developer. ROPE (Remote Object Proxy Engine) was the main player for the Version 1was and it was recognized as a COM component that serves several COM interfaces. However, with and within the ROPE, there were some setbacks and major defects. At the outset, not all of the required sections of the official SOAP specification were applied. For example, a vital element like encoding arrays of values was not possible. Even though with the ASP based server-side listener, you could write your own serialization/deserialization code, this was a frustration for a committed developer. The missing UTF-8 encoding of the XML message over HTTP was another irritating condition.
Taken as a whole, the Version 1 of the SOAP Toolkit was an unsupported MSDN code sample, and did not have a future in Microsoft. It had a few positive facts such as the source code availability. This future enabled you with enhancement for your personal needs. Therefore, December 2000 release was the last release and the SOAP Toolkit Version 2.0 (STK) took in that place.
Microsoft provides an official and supported product with the STK. It also plans to release at least some parts of the underlying source code. It can be used on Win 98 and higher and of course on Windows NT 4.0 and higher systems. Since the Windows 98 and Windows ME SoapConnectors use WinInet for communications and this comes with IE 5 and above, the STK has deployment and functional reliance on Internet Explorer 5 and on MSXML 3.0 SP1. Therefore when deploying your STK-based client applications, you need to install these two.
Achieving as high compatibility with the SOAP spec as possible was a crucial target for the development of the STK. So far, this goal has been achieved quite well. Actually, it is known that at some points the SOAP spec is too vague and as a result leaves too much room for SOAP implementers for their interpretations of the subject. As in Version 1, the STK provides several COM objects to interact with it. You can use two kinds of abstraction to the SOAP layer On the client. In most cases you can use the high-level interface, if you would like to all happen automatically behind the scenes and you do not have special requirements to the SOAP communication. The object to use is called SoapSampleClient.
As mentioned above, this will be enough in most cases. You need to initialize the SoapClient object by passing the required metadata about the SOAP based Web Service when you want to make an RPC like an operation with STK. This metadata resides in a WSDL (Web Service Description Language) file. You can simply call any of the Web Service methods directly on your SoapClient instance after initializing.
An example for Web Service that acts as a simple calculator:
oSOAPSampleClient = CreateObject("MSSOAP.SoapSampleClient");
oSOAPSampleClient.mssoapinit("CalcData.wsdl", "", "", "");
MsgBox oSOAPSampleClient.AddNumbers(20,10);
To describe SOAP messages and Web Services, WSDL is a typical, structured method. To create and interpret SOAP Messages, the STK is dependent on WSDL. Therefore, when using the high-level interface, a WSDL file is obligatory for any SOAP Toolkit 2.0 client or server. For every component or interface, a developer does not have to code the WSDL by hand. a tool called WSDL Generator wsdlstb.exe is provided by STK for command line usage and wsdlgen.exe for a GUI version. It takes a COM component as input and analyzes its type library. From these collected data, a new WSDL and WSML file is generated automatically.
In addition, SOAP has another method for communication that arrives with STK, called SMO (SOAP Messaging Object) framework, which is a document-oriented approach opposed to the Remote Procedure Call paradigm.
The client builds an internal representation of the WSDL in-memory after reading the WSDL for the SOAP endpoint. The methods are all dynamically published through IDispatch therefore like any other COM object the SAOP service can be used by any COM Automation capable client. When invoking such SOAP method (like Add (17, 4)) then the SoapClient object automatically serializes the data into a SOAP compatible XML message and sends the request to the SOAP endpoint. Currently, the transport protocol supported in 2.0 is HTTP, but it is intended to provide more protocols like SMTP or MSMQ. When returning from the SOAP service the SoapClient object deserializes the SOAP response and returns the appropriate COM data types to the calling client program. The depicted WSML is optional and only needed when transferring complex types using the STK's Complex Type Mappers.
As expected, things work similar on the server side. Initially, you have to choose between two types of endpoint implementation: ASP or ISAPI. The ASP solution is more flexible. You can inject pre and post processing code that can perform your desired operations. Of course, the ISAPI listener is much more powerful in means of performance and throughput. Additionally it is easier to use than an ASP page so unless you need to do something the ISAPI does not do you should always use ISAPI.
Moreover, as on the client side, you also can choose between the low-level or the high-level API. The details are nearly identical for both the ASP and the ISAPI listener. The server reads in the required WSDL and WSML. So what is WSML? It stands for Web Services Meta Language) and is a special XML file. One of the easiest ways to describe WSML is the analogy of a STK configuration file. It contains the information required to map the SOAP message described by a WSDL file to a COM interface. This includes the standard COM issues like ProgIDs. Other than WSDL, WSML will never be a standard because it only makes sense to the STK from Microsoft. If you like, you can think of WSDL as the public header (interface definition) file and WSML as the internal implementation file.
After that the incoming SOAP Envelope (message) is parsed and the appropriate XML elements are converted or deserialzed into corresponding COM types. Now the server object - named SoapServer for the high-level API used in ASP - can instantiate the COM objects and perform all needed operation on them. Based on each return value the server object serializes these values into a valid SOAP message, which will be passed back to the calling client.
To clarify these procedures have a look at a sample ASP endpoint:
<% Response.ContentType = "text/xml"
Dim SoapServer, WSDLFilePath, WSMLFilePath
WSDLFilePath =Server.MapPath("Calc.wsdl")
WSMLFilePath =Server.MapPath("Calc.wsml")
' error handling misplaced.....
Set
SoapServer = Server.CreateObject("MSSOAP.SoapServer")
SoapServer.Init
WSDLFilePath, WSMLFilePath
SoapServer.SoapInvoke Request, Response,
""
Set SoapServer = Nothing %>
This sample only delivers the incoming request directly over to the STK SoapServer object, which does all the essential work. The distinction if one uses the ASP or ISAPI solution is done in the WSDL file. Consider the following WSDL fragments:
<service name='SimpleCalc'>
<port name='SimpleCalcPortType'
binding='tns:SimpleCalcBinding'>
<soap:address location='http://localhost/SimpleCalcTest/Calc1.asp'/>
</port>
</service>
<service name='SimpleCalc'>
<port name='SimpleCalcPortType' binding='tns:
SimpleCalcBinding'><soap:address
location='http://localhost/SimpleCalcTest/Calc1.wsdl'/>
</port>
</service>
STK presumes the usage of an ASP listener when specifying an .asp extension in the location attribute. It uses the ISAPI based solution when specifying a .wsdl extension. You can go ahead, and build your SOAP based distributed application with these two techniques in mind and even more enchantingly, changes your existing ones.
An Introduction to SOAP Extensions
It is also exciting that you can create SOAP Extensions with the .NET Framework Web services technology. With these extensions, you gain access to the actual network stream before it is deserialized into objects within the framework, and vice versa.
SOAP Extensions allow developers to create very interesting applications on top of the core SOAP architecture found within .NET. For instance, on top of the Web Service call an encryption algorithm can be implemented. Alternatively, you could implement a compression routine, or even create a SOAP Extension that will accept SOAP Attachments.
Basically you can go into the SOAP XML, that .NET has created or just received with a SoapExtension class, and alter the contents or otherwise perform necessary processing. For example, with a SoapExtension, you are able to use XPath to extract a SOAP XML element, encrypt it, and return it to the XML stream before it leaves your computer. Or, when you receive a SOAP packet, to see if extra resources will be required to process the request, you can scan the contents and then, assemble those resources beforehand. Now, we will create an extension to record incoming and outgoing SOAP messages to our Web Service. This trace extension is useful for debugging when it is important to have the SOAP message appear exactly the way you want it to appear.
ProcessMessage method is the centerpiece of implementation that you need to be careful about it. This method is called several times, and it includes information on the stage of the SOAP message, every time it sends a SoapMessage object. For demonstration, we will look at the flow of a SOAP message within a Web Service. All of this applies on the client as well as the server, but for this example, we will concentrate on the server.
- A SOAP message comes in and the server decides which method to route to.
- Then the server checks to see if any SOAP extensions should be invoked, and if so, invokes them with the BeforeDeserialize event stage.
- Last, the server deserializes the stream and invokes all the extensions for the AfterDeserialize stage.
The BeforeDeserialize event stage is the key here. We also write it out to a MemoryStream so when we are done; the SoapMessage object still has a valid stream. If this is not done, the server will try to deserialize the network stream that was originally on the SoapMessage, and find nothing there. When we log the SOAP message a similar process takes place, and the server will return to the client. However, there is one exception. In the BeforeSerialize phase, we need to switch the network stream on the SoapMessage, and replace it with one of our own MemoryStreams. This will allow us to record whatever is sent as SOAP, without immediately losing it in the network. We record all that was written to the memory stream, and once we are done write it out to the network stream. The client will never get a response if we do not write it out to the stream we stole. Here is the full trace extension for you to use:
<%@ WebService Language="c#" Class="DoIt" %>
using System;
using System.IO;
using System.Text;
using System.Web.Services;
using System.Web.Services.Protocols;
public class DoIt
{
[WebMethod]
[TraceExtension()]
public Address SampleMethod()
{
Address address = new Address();
address.Street = "14 Royal Crest Dr.";
address.City = "North Andover";
address.State = "MA";
address.ZipCode = "01845";
return address;
}
}
public class Address
{
public String Street;
public String City;
public String State;
public String ZipCode;
}
[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
public override Type ExtensionType
{
get { return typeof(TraceExtension); }
}
}
public class TraceExtension : SoapExtension
{
Stream oldStream;
Stream newStream;
public override int GetPriority()
{
return 0;
}
public override object GetInitializer(LogicalMethodInfo methodInfo,
SoapExtensionAttribute attribute)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
switch (message.Stage)
{
case SoapMessageStage.BeforeSerialize:
SetStream( message );
break;
case SoapMessageStage.AfterSerialize:
WriteOutput( message );
break;
case SoapMessageStage.BeforeDeserialize:
WriteInput( message );
break;
case SoapMessageStage.AfterDeserialize:
break;
default:
throw new Exception("invalid stage");
}
}
public void SetStream( SoapMessage message )
{
oldStream = message.Stream;
newStream = new MemoryStream();
message.Stream = newStream;
}
public void WriteOutput( SoapMessage message )
{
newStream.Seek(0, SeekOrigin.Begin);
Double now = System.DateTime.Now.ToOADate();
FileStream fs = new FileStream("c:\\log.txt",
FileMode.OpenOrCreate, FileAccess.Write);
// create a Char writer
StreamWriter w = new StreamWriter(fs);
// set the file pointer to the end
w.BaseStream.Seek(0, SeekOrigin.End);
TextReader reader = new StreamReader(newStream);
string line;
while ((line = reader.ReadLine()) != null)
{
w.WriteLine(line);
}
w.Flush();
w.Close();
newStream.Seek(0, SeekOrigin.Begin);
Copy( newStream, oldStream);
}
public void WriteInput( SoapMessage message )
{
MemoryStream m = new MemoryStream();
Copy( message.Stream, m);
m.Seek(0, SeekOrigin.Begin);
Double now = System.DateTime.Now.ToOADate();
FileStream fs = new FileStream("c:\\log.txt", FileMode.OpenOrCreate,
FileAccess.Write);
StreamWriter w = new StreamWriter(fs);
// create a Char writer
w.BaseStream.Seek(0, SeekOrigin.End);
// set the file pointer to the end
TextReader reader = new StreamReader(m);
string line;
while ((line = reader.ReadLine()) != null)
{
w.WriteLine(line);
}
w.Flush();
w.Close();
m.Seek(0, SeekOrigin.Begin);
message.Stream = m;
}
void Copy(Stream from, Stream to)
{
TextReader reader = new StreamReader(from);
TextWriter writer = new StreamWriter(to);
string line;
while ((line = reader.ReadLine()) != null)
{
writer.WriteLine(line);
}
writer.Flush();
}
}
You derive a new class from SoapExtension and override the necessary virtual methods to create a SoapExtension. To hook the extension into your Web methods, you also create a custom attribute based on Soap ExtensionAttribute.
An Overview to The COM Language Binding
Interoperability with any other remote system is the most important intention for SOAP. It is extremely scalable and does not have any implicit support for system security or object identity and all of these conflicts with the primary objectives of DCOM. The solution to this problem is to renounce the goals of DCOM and use only objects that match the goals of the SOAP architecture. If you look at the other language binding, they create a connection with the remote computer, executes the remote method, and return the results to the caller.
For subsequent calls, there is no precondition like transactional cookie or some other similar mechanism. You call and ask the remote computer to do something for you, and then end the connection. While this security is a non-trivially considered and addressed by the remote server, this architecture also does not offer a full security performance.
Architectural View
The Application layer is the true object client in the vital components of the client-side processing architecture. The application program or other object invokes the remote object. The Even if an interactive application may present the user with a delay as the remote object is accessed Application layer may or may not be aware of the object, which it is using, has been contacted remotely. To cooperate with the SOAP features of the remote method call, the processing architecture exposes an interface. At this time, the interface provides the namespace and the endpoint URL that are being used, in addition to a method to create a new object with SOAP support delegation. The architecture relies on the SOAP Catalog and type library encoding objects.
As well as a method to create a new object with SOAP support, the interface provides the namespace and endpoint URL that are being used. The architecture relies heavily upon the SOAP Catalog and type library encoding objects. The SOAP Catalog is used to record which object/interfaces are to be remoted and to store the germane endpoint URL and namespace associated with the object and/or interface. Without the associated type information, The SOAP encoding is not possible. This is the function of the type library encoder. Client and server side architectures share both the SOAP Catalog and type-library encoder objects. The serializer object handles the essence of the SOAP work. Representing SOAP, processing the serializer object acts as the state machine and it encodes the type library, creates SOAP request packet, ships the packet to the remote server, interprets the method results, and finally returns those results to the application layer.
The server side architecture mirrors the client-side architecture in that it has to deserialize the SOAP payload in order to reconstruct the original method call. in addition, to tie SOAP into the Web server, ISAPI extension is used. For integration with the IIS Web server ISAPI extensions provide a robust environment, on the other hand, in this architecture ASP could have also been used.
Figure 3.2.The COM language binding client-server architecture
When the ISAPI extension receives a SOAP request, it forwards the call to the activation code for processing. The activation code collects relevant method information as needed and deserializes the SOAP payload. To interpret the incoming call, both the server side and the client side rely heavily on the SOAP Catalog and type library decoding objects. The activation code generates the client-requested object and obtains the appropriate interface, presuming the request is intended for a publicly exposed interface. Given the method signature, while creating a call stack from the list of method parameters, the activation code parses the SOAP payload, then executes the object's method and retrieves the method results from the stack. Then, a SOAP response can be constructed, which contains all the necessary outgoing method parameters and the return code. This is sent back to the client for final processing.
Interception and Delegation
Transactional objects are created as any other object at the beginning, and after they have been created and debugged, you register them with MTS. When an object is registered, MTS takes it as an instance is being created before that instance is returned to you. MTS grasps the object's creation. Obviously, this is important for providing the transactional environment that is required. This environment is an integral part of the COM runtime environment to your object. You need to meet the transactional methods when passing out object references, which observable extensions contain.
You also need to design your objects as stateless because they could be contributing in many transactions at once. Essentially, when implementing COM based interception architectures MTS sets interesting precedence in two areas. One of them is an object designed for transaction processing is an ideal candidate for SOAP processing. However, more importantly, the MTS interception model is deal for creating instances of remote objects. Once your object is registered with MTS, in order for MTS to create your object, it must tread between your application and COM. MTS adjusts the Registry for your object to be able to accomplish this. In particular, it removes the path to your object from the Registry and stores it in the MTS Catalog. COM reads the Registry when you create and instance of your object, and creates a special MTS object as replacement, which in turn creates an instance of your object. Therefore, between your application and the object you want to use MTS effectively places itself.
On the other hand, when MTS is in consign between your application and the object, MTS can manage the environment in which the object executes. As an instance, MTS makes available an alternative environment, which can be accessed to retrieve the transactional and security characteristics of the object's lifetime. To help with its transactional processing, MTS also provides other services your object can call upon. MTS intercepts your object and controls the object at a higher level than otherwise possible because of its previous actions. Naturally, there is a lot involved with this, and the transactional objects must obey certain rules as well. For instance, the object cannot simply provide its own interface pointer for other objects to use. Instead, it has to rely upon MTS to properly marshal the interface pointer and context. Consequently, interception is not a perfect solution, but it has its place.
As a final note, with COM terms, delegation and interception are alike in that intermediary code inserts itself flanked by the client and the server object. The primary differences are the same. MTS intercepts the entire object instead of interface to the object exports. With Delegation, you have a better control, and instead of the entire object, you only delegate a single interface. In addition, Delegation is traditionally a cooperative effort. To create an instance of the delegated object, the client code usually invokes a third party method. Alternatively, it gives an existing interface pointer to the delegator and enables the delegator to deal with the object as required. Therefore, the client becomes aware of the delegation, and it can take measures to develop or disregard the delegation.
Summary
In this article, we have covered several important issues about SOAP. We started with an overview and then we studied SOAP messages, Envelopes, body, header etc. As well, we saw SOAP serialization, RPC and finally COM language binding as a theoretical overview. In the next article, we will have Reusable SOAP C++ and relational subjects such as Creating and Registering a COM Component, Accessing the COM Component's Methods and Properties.