+ 2 - 2 | § ¶Why is XmlSerializer so limited?
XmlSerializer has some limitations that some people find annoying. For example, XmlSerializer must know at construction time all types that are going to be serialized. So, if the class you want to serialize has a member of type ArrayList, the types of the objects that it can contain must be declared using the [XmlArrayItem] attribute, or by using [XmlInclude] in some class, or by providing an array of those types to the constructor of XmlSerializer.Another limitation is that the classes you want to serialize must be public. That means, for example, that if you are implementing an assembly and you want to use XmlSerializer to serialize some internal classes, you can't.
The reason for these limitations and other you can find is a performance/functionality tradeof. XmlSerializer was designed to be fast, because it is the engine of all Web Service infrastructure. All Soap messages being sent from a client and received on a server are serialized and deserialized using XmlSerializer.
So, to be as fast as possible, XmlSerializer generates and compiles serialization code on the fly. That's why it must know in advance all types that are going to be serialized: it has to generate serialization code for each of them. And that's why all classes must be public: if not, the generated code could not create the instances of the types (yes, it could use reflection, but "Activator.CreateInstance (typeof(MyType))" is much slower than "new MyType ()")
However, I've heard that some of this will be supported in Whidbey. I'll find out next week.
+ 1 - 2 | § ¶Interception for everybody
I was once thinking about how System.Enterprise.Services could be implemented in Mono, and more specifically, how could we implement support for implicit transactions. Implicit transactions means that when an app opens a database connection from inside a transactional method, the runtime should start a transaction on that connection, and should commit or rollback it when the app leaves the method. The problem with this model is that there is no link between the database connection and the runtime, since the app creates the connection on its own.
So my question was: how can the runtime know that a database connection has been created and opened? The good answer is that the database connection object must communicate with runtime (that's more or less what MS does). It means that the runtime should provide some standard API for this, and the database connection should use it. Right now, there is no such standard API, but we should define one if we really want portable support for implicit and distributed transactions. But well, that's another story...
That problem made me think about the possibility of using some kind of interception to detect, for example, when a SqlConnection is created or when methods like Open() and Close() are called. This way, the runtime could manage the transactions behind the scenes. Although this did not completely solve the issue, it was a funny challenge.
The class ContextBoundObject provides method call interception through several kinds of interception sinks that you can define. However, it has several limitations: any class that do not derive from ContextBoundObject cannot make use of interception sinks, and the class needs to explicitely define what can be intercepted and what the interception does. That means that you cannot add interception to classes that are not designed for it.
But looking at a lower level, Remoting provides all necessary for interception. It provides TransparentProxy and RealProxy that can transform a method call to a message, and provides an infrastructure of channel sinks that can process that message. Remoting can also intercept object creation (for example, when you instantiate a class configured as "Client Activated").
Ok then, we only need to put all the pieces in the right way, and that's what I did. The result is a nice Interception class (here is the code), which allows you to do thinks like in the following example:
There are other methods for activating interception for methods and types. Look at the code for more info.
Before you get too excited about the beauties of this hack, be warned of some limitations:
- The classes you want to intercept must derive from MarshalByRefObject
- Those classes CANNOT be used in remoting scenarios, that is, interception won't work for objects you want to access through remoting.
- Calls may be slow for some applications, since all calls go through the remoting infrastructure.
How does it work?
The trick is to create a custom "interception" remoting client channel (InterceptorChannel in the code). The Interception channel is like any other client channel, but instead of packaging and sending messages to a server, it forwards the messages to a local object, but providing a chance to intercept and process them before doing so.
In fact, the channel is very simple. All processing is done in the formatter sink (InterceptorSink). It is the first sink in the channel sink chain, so it is a good place for intercepting the messages. When the sink receives a construction message, it creates the object and registers it internally. It also generates an uri for the object, which will be used later to locate the object when a message is received.
The channel only uses standard remoting stuff, so it works in Mono as well as in MS.NET.
Enjoy!
+ 2 - 1 | § ¶Even more web service features
After some days of intense hacking I finished the support for RPC format in the wsdl importers and exporters. This means that with Mono it is now possible to implement and consume web services, generate wsdl documents and client proxies for all valid protocol and transport combinations:
- Soap Protocol. Support for:
- Document style: both encoded and literal format, using wrapped or bare parameter style.
- RPC style: encoded format using wrapped parameter style.
- Http Get: request encoded in the query string. Response in xml literal format.
- Http Post: request encoded in the body as form values. Response in xml literal format.
I also improved a lot the web service documentation page. Information is now better organized, it has syntax coloring for code and xml, and it can generate Visual Basic proxies (altough VBCodeProvider fails to generate valid VB code, it needs some fixes). You must try it!
Next focus is test. We need a good test suite for our WS implementation, which tests interoperability not only between Mono and .NET but also with any platform that provides WS.
+ 3 - 1 | § ¶Playing with Soap Extensions, Part II: Extending WSDL support
In a previous article I explained how to use Soap Extensions to extend and customize the process of sending and receiving Soap messages. But for all this to work, the server needs to tell the client which extensions it is using. The place to declare this information is the WSDL document, the document that acts as a contract between de client and the server.
WSDL was designed to be extensible, and it permits the addition of arbitrary information elements in several sections of a document: service, port, binding, operation binding and message binding. In the Compression example, we need to specify in the WSDL document which methods expect messages to be compressed. The best place to add this information is the operation binding of each method. So, for a test web services like this (notice that the method GetCurrencyInfo() has the [Compress] extension attribute), we'll get a wsdl document like this:
.NET generates this WSDL document on the fly by inspecting the methods of the web service using reflection. In order to add that mono:compress element we need to extend this generation process.
Creating the extension element
The first step is to create the class that will hold the information for the mono:compress element. It looks like this:
The class must derive from ServiceDescriptionFormatExtension and it must have the XmlFormatExtension attribute applied. This attribute specifies the name and namespace that have to be used when serializing the element, and the type of WSDL section where this extension element can be applied. The XmlFormatExtensionPrefix attribute is optional and you can use it to specify the prefix for the element namespace.
In the System.Web.Services.Description API, all classes that can include additional elements have the XmlFormatExtensionPointAttribute attribute applied, and they have a Extensions property (a collection of ServiceDescriptionFormatExtension) where we can add our extension elements. However, we can't directly add our extension elements to the WSDL document, because we don't have any WSDL for the service, since it is always generated on the fly. So how can we add them? by hooking into the process of generating the WSDL document.
Extension reflectors
The next step is then to create a class derived from SoapExtensionReflector. SoapExtensionReflector has only one method that you must override: ReflectMethod. While the runtime is building the WSDL document from the web service, it calls ReflectMethod for each method of the web service (and for each extension reflector we have configured). You can get the method being reflected from the ReflectionContext property. This property also provides access to other useful information such as the reflected type, the ServiceDescription being built, the Service, the Port, the PortType, the Binding and so on.
In the compression example, ReflectMethod checks if the method has the Compress extension attribute, and in this case it adds an instance of our CompressOperationBinding class to the extension collection of the operation binding. It is pretty straightforward:
Implementing a SOAP extension importer
With the extension reflector the runtime can now generate a WSDL document with our mono:compress elements. The last step is to extend the client proxy generator, and make it add the appropriate [Compress] attribute to the methods that have the compress element in the wsdl document.
We need to create a subclass of SoapExtensionImporter and implement the method ImportMethod(). It will be called by the runtime for each method declared in the wsdl document. In this method we should look for our compress extension element in the wsdl document, and if found add the Compress attribute to the method. The class would look like this:
In this example, metadata is the list of attributes of the method being imported. Through the inherited ImportContext property you can access to interesting information such as the operation binding being imported, as well as the service, the port, the binding, etc.
Configuring the whole thing
We need to register our extension element, extension reflector and extension importer in machine.config or web.config for all this to work. Notice that the importer needs to be configured where the wsdl tool will be invoked (usually in a client), while the reflector must be configured where the web service is running. The extension element needs to be configured in both sides (it is used by the reflector and by the importer). This example shows how all this info would be configured in a single file:
Notice that you can use the prioriy and group attributes to specify the order in which importers and reflectors are invoked. The rules are the same that apply for soap extensions (explained in the previous article).
Oh, by the way, if you generate the proxy from the web service documentation page you won't need to configure the extension element and the extension importer in the client, since all runs in the server. However, that's something you can only do with Mono
+ 1 - 2 | § ¶GetType() is slow!
Just realized how slow is a call to Object.GetType() compared to the "is" keyword. It turns out that this method:
is 9 or 10 times slower than:
Well, ehem, in fact a single value.GetType() call is 9-10 times slower than the whole collection of ifs. And I was thinking that GetTypeCode() was cool because you could switch on the result...
+ 2 - 3 | § ¶Playing with Soap Extensions, Part I: gzipped soap messages
Since the 0.28 release, Mono has complete support for Soap Extensions. The use of soap extensions is rather undocumented, specially what's related to WSDL extensions, so this is an interesting subject to write about.
I'll try to explain how all this works using a simple example: a compression extension. With this extension it will be possible to compress the messages being sent or received from the server, so we can save some bandwidth.
Soap Extensions allows an application to hook into the process of sending or receiving a Soap message, so the application can transform it, perform validations or do whatever it needs. Those extensions can be added in the client side (in the process of sending a request and receiving a response) and in the server side (in the process of receiving a request and sending a response). In the compression example, the extension is needed in both sides, since we need to compress the message in the client, and decompress it in the server.
Notice that in this example the client and the server must agree about the use of the extension. The server must somehow declare that it expects the messages to come compressed. The place to declare it is the WSDL document. A Web Service uses a WSDL document to describe the methods that it provides, the protocol to be used to reach the service and the format of the messages, among other things. Moreover, a WSDL document can be extended at some points to include custom information that describe particular extensions implemented in the service. For example, a WSDL binding could be defined like this:
Notice the mono:compress element. It is telling a potential client that the server expects messages for the operation "GetCurrencyInfo" to be compressed.
To implement an extension in Mono (and in the .NET Framework) you need to do the following:
- Implement the SOAP extension that will comporess and decompress messages.
- Implement a SOAP extension reflector, that will add the appropiate extension elements to the WSDL document.
- Implement a SOAP extension importer, that will hook into the process of generating a web service client proxy and add custom code or custom attributes.
I'll cover the last two points in another article, this one is already too long. Now, let's see how a Soap extension can be implemented ...
(more)+ 2 - 1 | § ¶PDC, I'll be there
I'm going to the PDC, together with Gonzalo and Miguel. There are lots of sessions about Whidbey (.NET 2.0) and Indigo (who knows what is this?).I've been once in the PDC, in the 2000 edition, where .NET was presented for first time. I really enjoyed it, but things have changed a lot for me since then...
+ 1 - 2 | § ¶Using OutAttribute in Remoting
One of the greatest features of Remoting are the proxies, which allow you to use a remote object like if it was an object running in the client. All calls to the proxy are magically converted to messages which are sent to and executed in the remote server. Even access to public fields are transparently remoted, which makes the ilusion of having a local object more real. Moreover you can specify that a given type must run in a remoting server using a configuration file, so you don't need to modify anything in the your application, it will just run like if those objects were local.
This is a nice feature, but somehow dangerous. A class whose instances will be accessed through remoting cannot be designed like if they were going to be used locally. The main reason is performance. It is OK for to make 10 calls to get information from a local object, but if the object is remote, you should have one method than can return all info in a single call.
Another reason is that input parameters are allways sent by value to the server, that is, remoting sends a copy of the parameters, so if the server modifies the contents of the parameter, that change won't be sent back to the client. So, for example:
This app will work if TestClass is created locally, but it will fail if the object is created in a remote server.
That's why, when I was tracking down one bug in XSP, I though that the problem was that XSP was using a stream across app domain boundaries. The Read method of Stream take as parameters a byte array, a position and a size. The byte array is sent by value to the remote domain, where it is filled with the stream's data, but that data is not serialized back to the client, just like in the previous example.
To make sure I was right I made some tests with the MS.NET and, oh! surprise! it did work! The client byte array was filled with the data read in the server. But the byte array is an input parameter, not byref, so why does MS.NET serilalize the data back to the client in this case?
After some research I found that the responsible of all this is the attribute System.Runtime.InteropServices.OutAttribute. The MS.NET documentation says that the OutAttribute "Indicates that data should be marshaled from callee back to caller". It makes sense. But it also says that "This attribute only applies in COM interop and platform invoke scenarios" which is not true, because it also works in remoting, at least in some cases.
In fact, it seems that in remoting it only works for array parameters (and that's why it works for Stream.Read). It means that the following example will work even if TestClass is remote:
This is now working in Mono
+ 1 - 2 | § ¶Mono and SourceGear
We are done with the SourceGear project!Thanks to this project we now have a robust implementation of xml serialization and web services. We have also fixed several memory leaks and the runtime is now more stable. I hope we can get more projects like this one!
+ 1 - 2 | § ¶More cool features in web services
I just commited support for extensions in WSDL generation and also in WS proxy generation. This feature is rather undocumented, so I'm preparing a nice article explaining how this works.I also commited partial support for HttpGet and HttpPost protocols, enough to make work the test form of the WS documentation page.