«
October 2002
»
XML-RPC Error Reporting
Wednesday 30 October
I've received a couple of emails recently about problems with XML-RPC.NET which turned out to be problems with the XML-RPC services being used. As it happens I've been doing some work on improving error reporting which will improve diagnosis of problems like these, but with the main focus being on reporting errors in response or request parsing. Where complex data structures consisting of nested arrays and structs are used - and some of the structures people are using are very complex indeed - it can be difficult to work out why the response or request fails to map onto the .NET types used in the proxy or server implementation. The changes involve dumping the state of the parse stack when an error occurs along with a description of the error.
For example, this morning I wrote some code to call the NewsIsFree service. The structure for each news item contains a date member, described as "the timestamp at which the news item was added to the database". So I defined this structure to represent a news item, using a DateTime type because I expected the date member in the response to be a dateTime.iso8601 value:
struct NewsItem
{
public string title;
public string link;
public DateTime date;
}
I made a call, using the latest dev build of XML-RPC.NET, to the hpe.getSource method and received an exception containing this message:
response contains int value where dateTime expected [response : struct mapped to type Source : member items mapped to type NewsItem[] : array mapped to type NewsItem[] : element 0 : struct mapped to type NewsItem : member date mapped to type DateTime]
This provides enough information to determine that the date member is actually returned as an integer.
Note: in case anyone else is affected, the services currently with problems are:
Advogato: diary.getDates returns an invalid XML-RPC response
NewsIsFree: hpe.getSources returns an empty HTTP response
XML-RPC.NET Service Auto-Documentation
Friday 25 October
I'm currently working on working on the auto-documentation feature of XML-RPC.NET services. This is similar in concept to what you see when you point your browser at a .NET WebService. Reflection is used to extract information from the interface defining the service, in particular from custom attributes defining documentation strings.
I've updated the sample Math Service to demonstrate the current prototype. The page is generated from the following code:
public interface IMath
{
[XmlRpcMethod("math.Add",
Description="Add two integers and return the result")]
[return: XmlRpcReturnValue(Description="A plus B.")]
int Add(
[XmlRpcParameter(Description="first number")]
int A,
[XmlRpcParameter(Description="second number")]
int B);
...
}
Currently the attributes describing the service have to be placed on the service class:
[XmlRpcService(
Name="Test Math Service",
Description="This is a sample XML-RPC service illustrating method calls with simple parameters and return type.")]
public class MathService : XmlRpcService, IMath
{
...
}
Items on the todo list for this include working out how to describe XML-RPC structs which don't have a fixed set of members and the other dynamic features of XML-RPC which don't map well onto static data types; and providing examples of the XML request and response documents for each method (as per .NET WebServices).
Version 0.6.0 of XML-RPC.NET
Sunday 20 October
I've just uploaded version 0.6.0 of XML-RPC.NET. This contains the changes listed below. I've not yet updated the docs. This will take another week or two.
- Added XmlRpcProxyGen class to dynamically create a proxy object from an interface, i.e. makes hand-coding of proxies unnecessary in most cases. bettyapp sample changed to illustrate this.
- Can now use an interface to define XML-RPC methods. Can use the same interface to implement both server and client. MathService changed to illustrate use of interface.
- Default for XML-RPC request and response XML documents is now no explicit encoding, i.e. implicitly UTF-8. Previously the encoding was explicitly specified as UTF-8.
- Added Encoding property to XmlRpcClientProtocol to set explicit encoding on XML-RPC request and response XML documents.
- Added Proxy property to XmlRpcClientProtocol.
- Fixed UserAgent property of XmlRpcClientProtocol.
- Fixed parsing of double type to be culture independent.
- Fault response XML document now generated in same way as ordinary response, i.e. will be in same format and encoding.
Amazon UK Slipping Badly
Friday 18 October
Amazon UK is slipping badly. My last order was slow in being dispatched and I gave them the benefit of the doubt. But an order I placed this week confirms they are running the business in a different way these days.
I placed the order on Monday morning. Two books were on 24 hour availability and a third book on 2-3 days. Estimated dispatch was Thursday. It used to be the case that when books were available the order details changed to show them as being on-hand. It may have been fluff but it gave you the impression that something was happening with the order.
By early Tuesday morning the third book had changed to 24 hours availability so I expected the dispatch date to change. Nothing happened. On Thursday morning I cancelled the order and input another order for the same books. This time the order quickly changed to Dispatching soon, the dispatch date being the same day, expected delivery on Friday.
It is now Friday evening and the order status is still Dispatching soon. But the dispatch date is still Thursday with delivery for Friday. Something is seriously wrong! Coupled with minor display errors on the order information page, it means the fairly unique shopping experience they offered, in particular the near instant gratification of placing an order one day and always receiving it the next day, is no more.
VsDebuggerCasualtyData and XML-RPC.NET
Friday 11 October
Mike Tuyo emailed me about a problem which can occur when using XML-RPC.NET on the client side if the XML_RPC server makes assumptions about the size of the HTTP request: if running the library in debug mode from within Visual Studio HTTP requests contain a VsDebuggerCasualtyData header. This is very large and cause problems if the server assumes a max size of the HTTP request, for example 2048 bytes (the buffer size that caused Mike's problem).
XML-RPC.NET on the Compact Framework?
Thursday 10 October
Quite by accident I came across a page listing errors received when attempting to compile XML-RPC.NET for the .NET Compact Framework. Bryce Yehl's blog entry which links to this page says:
Anyone out there familiar with XML-RPC.NET? I am having some difficulty compiling it against the Compact Framework. My changes and compile errors (primarily removing XML-RPC Server stuff, the CF's System.Web is incomplete). Any help greatly appreciated...
I've not looked at the .NET Compact Framework at all. Looks like there would be a fair amount of work in porting XML-RPC.NET.
(I just looked through the archives of the XMLRPCNET Yahoo group and Bryce did indeed post a question about the Compact Framework at the end of July. Looks like none of the other group members have done anything in this area.)
Interfaces and XML-RPC.NET Services
Wednesday 9 October
Before releasing the version of XML-RPC.NET with support for interface-based automatic generation of proxy classes, I'm enhancing the server side of the library to allow services to be defined via interfaces.
At the moment a service is implemented like this in C#:
public class MathService : XmlRpcService
{
[XmlRpcMethod("math.Add")]
public int Add(int A, int B)
{
return A + B;
}
}
The custom attributes are specified on the methods of the class and it is not possible to use an interface (which could then be used in proxy class generation). So I'm currently modifying the XmlRpcServiceInfo class to check whether methods are derived from an interface and if so to check the interface methods for the custom attributes. This will allow a service to be implemented like this:
interface IMath
{
[XmlRpcMethod("math.Add")]
int Add(int A, int B);
}
public class MathService : XmlRpcService, IMath
{
public int Add(int A, int B)
{
return A + B;
}
}
XML-RPC.NET Service in VB.NET
Monday 7 October
Charles Nadeau recently asked (on the XMLRPCNET group) for an example of an XML-RPC.NET service written in VB.NET. I'm doing some work on the XML-RPC.NET FAQ this evening and added the following sample code:
Imports CookComputing.XmlRpc
Public Structure SumAndDiffValue
Public sum As Integer
Public difference As Integer
End Structure
Public Class SumAndDiffService
Inherits XmlRpcService
<XmlRpcMethod("samples.sumAndDifference")> _
Public Function SumAndDifference(ByVal x As Integer, _
ByVal y As Integer) _
As SumAndDiffValue
Dim ret As SumAndDiffValue
ret.difference = x - y
ret.sum = x + y
Return ret
End Function
End Class
Example of XmlRpcProxyGen
Sunday 6 October
An example of how the new XmlRpcProxyGen class is used. First define an interface which represents the methods exposed by the XML-RPC server, marking the interface with the URL of the server if required. Each method is marked with the XmlRpcMethod attribute which allows the name of interface to be mapped onto the XML-RPC server method. For example:
[XmlRpcUrl("http://betty.userland.com/RPC2")]
interface IBetty
{
[XmlRpcMethod("examples.getStateName")]
string GetStateName(int stateNumber);
[XmlRpcMethod("examples.getStateStruct")]
string GetStateNames(XmlRpcStruct request);
}
Second, call the static Create method of XmlRpcProxyGen to create an instance of a dynamically generated class which implements the interface and derives from XmlRpcClientProtocol:
IBetty betty = (IBetty)XmlRpcProxyGen.Create(typeof(IBetty));
Finally call one or more of the interface methods:
string stateName = betty.GetStateName(3);
XmlRpcStruct stateNumbers = new XmlRpcStruct();
stateNumbers.Add("state1", 1);
stateNumbers.Add("state2", 2);
string stateNames = betty.GetStateNames(stateNumbers);
I'm hoping to issue a new release of XML-RPC.NET containing XmlRpcProxyGen sometime over the next day or two.
Refection.Emit
Saturday 5 October
Reflection.Emit is cool. For example, the XML-RPC.NET proxy generator has to add a custom attribute - XmlRpcMethodAttribute - to each method generated in the proxy class to specify the name of the method exposed by the XML-RPC server endpoint (the .NET method name is not necessarily the same as the method name on the XML-RPC server). I thought this might be tricky to accomplish but it was in fact trivial:
// create MethodBuilder for the method being defined
MethodBuilder mb;
...
// get the type of the custom attribute
Type customAttr = typeof(XmlRpcMethodAttribute);
// the parameter signature of the attribute constructor to be used
Type[] oneString = new Type[1] {typeof(string)};
// get a ConstructorInfo object for the required constructor
ConstructorInfo ci = customAttr.GetConstructor(oneString);
// get a CustomAttributeBuilder object, specifying the constructor parameters
CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[]
{XmlRpcMethodName});
// set the custom attribute on the method
mb.SetCustomAttribute(cab);
CIL Programming
Saturday 5 October
Peter Drayton recently made some Observations on CIL books. He summed them up very well. For most developers I think CIL Programming: Under the Hood of .NET would be the best buy because its an introductory text and is much more readable as a result. It contains a lot of detail on Reflection.Emit which is proving very useful in the XML-RPC.NET work I'm doing this weekend: implementing code to automatically generate XML-RPC.NET proxies. Judging from the emails I've replied to over the last few months implementing a proxy causes the most problems and the new proxy generator will make this much easier. Define an interface to represent the XML-RPC end-point and then use it to generate a proxy. Using the proxy generator will look something like this:
interface IFoo
{
int Add(int a, intb);
}
IFoo fooProxy = XmlRpcProxyGen.Create(typeof(IFoo), "http://localhost/math.rem");
int result = fooProxy.Add(1, 2);
Alternatively it will be possible to save the proxy to disk as an assembly. So far I've got a prototype of the generator working and I'm now working out the details of how it will fit into the XML-RPC.NET library.
Multiple Inheritance and Envision Eiffel
Wednesday 2 October
How does Eiffel support Multiple Inheritance in the .NET environment? After all, the CLR does not support MI for classes. But it does support MI for interfaces and this is the key to the solution.
As mentioned in an earlier entry here, each Eiffel class is represented by an interface and two classes when compiled into a .NET assembly. The interface is the type used to represent the class in client code, the Create class provides a static method to instantiate instances of the class, and the Impl class is the implementation of the class.
Suppose we have an Eiffel class called CHILD inheriting from classes PARENT1 and PARENT2. In the generated .NET assembly there will be three interfaces: Parent1, Parent2, and Child which derives from both of the parent interfaces.
Once an instance of Child has been acquired, it can be cast to Parent1 or Parent2 which simulates upcasting to the base class:
Child child = Create.Child.Make(); Parent1 p1 = (Parent1)child; Parent2 p2 = (Parent2)child;
This works because class Impl.Child implements interface Child, which implies the class, or one of its base classes, implements interfaces Parent1 and Parent2. The obvious question which follows from this is: if MI is not supported in .NET, how can methods in the parent classes be invoked? Say PARENT1 has feature Foo and PARENT2 has feature Bar, how does the following work:
Child child = Create.Child.Make(); Parent1 p1 = (Parent1)child; p1.Foo(); Parent2 p2 = (Parent2)child; p2.Bar;
The answer is that Impl.Child implements all the methods of the Parent1 and Parent2 interfaces so avoiding the need to call the methods in the base classes. The methods are of course also implemented in the base classes Impl.Parent1 and Impl.Parent2 so there is a some duplication of code within the assembly. Impl.Child itself only derives from Object and so has no relationship with the parent Impl classes.
Out of curiosity I tried creating an Eiffel class which inherited from two external .NET classes implemented in C#. It failed to compile with this error message:
Error: class inherits multiply from classes that are .NET external classes or that inherit from .NET external classes.
Assembly References and Envision Eiffel
Wednesday 2 October
Its worth mentioning that referencing local assemblies doesn't work in the current version of Envision (v1.0). The workaround to this is to add the assembly to the Global Assembly Cache (GAC). For this to work the assembly must be signed.
Precompiled Libraries with Envision Eiffel
Wednesday 2 October
Ive had some problem building a precompiled library with Envision. The following sequence of steps seems to work for building a simple precompile project. A different sequence, such as adding a class to the project before specifying a cluster seems to result in a project which wont build.
From the New Project dialog select Precompile Project.
On the Precompile Project Wizard indicate whether you want to use the eBCL precompile. This is a precompilation of the base class library which speeds up project builds. If you dont use the precompile one or more clusters are specified for the project according to what you select in Include Library option. Using clusters means you are including additional source files in the project and builds will take correspondingly longer. In either case once the project has been created you can change specification of the precompile and/or clusters.
Next, specify a cluster in which new classes will be placed. Open the Property Pages for the project and go to the Clusters page. Add a cluster and specify its path as the project directory (this is where new classes are placed by default),
Add one or more classes to the project.
Finally, build the project. The precompiled library will be a file with a .epr extension and will be in the debug or release directory under the project directory.
To use the precompiled library in another project, open the Property Pages for the project at the General page. The Precompiled Library Path will have defaulted to, for example, $ISE_EIFFEL\ebcl\debug. Change this to the location of the precompiled library built above.
Obviously there are a lot more choices to be made when deciding on what should go into a precompiled library but the above was sufficient to demonstrate to me how precompiles work in principle.