Tuesday, March 26, 2013

Failed Web Service Call Due to Big Time Difference Between Servers

The other day, a working web site was suddenly down. Its registration and login related functionalities were all failed.
The error was caught but the error message was kind of “Unknown”. Dug to the code, the caught exception “e” in below code is a null:
   1:  try
   2:  {                   
   3:      return client.Proxy.IdentityTypeList();
   4:   }
   5:  catch (Exception e)
   6:  {
   7:      this.HandleException(e);
   8:   }

what line 3 does is calling a web service, but it was failed and throwing an null exception. As there was no message text at all, it seemed it was getting into a dead end in for trouble shooting.

It finally turned out it was caused by there was a big time different between the tow machines (the calling web server and the web service server). The difference was more than 5 minutes.

What I have done to find out the cause was I wrote a test program in C#. The test program was a WinForm application. The function of the application was to call the same web service as above code did.

When the application was deployed to the web service, it failed to call the web service and threw an exception. This time the exception did have a error message text complaining that the web service returned in future time and the time in the future was longer than 5 minutes.

What is still mystery to me from the above case is that the same code in Web application and WinForm application throws different exception. The exception threw in Web application does not make sense at all which make it very difficult to do the trouble shooting.

Tuesday, March 12, 2013

XML Data Binding in Delphi 2007 (2)

In my previous blog [XML Data Binding in Delphi 2007 (1)], I have described there were issues when using XML Data Binding facility in Delphi 2007.

We are going to solve name space issue in this blog.

Let’s have a look at the schemas we are going to map.

Book.xsd:

  1: <?xml version="1.0" encoding="utf-8"?>
  2: <xs:schema targetNamespace="http://www.bochenlin.com/book"
  3:     elementFormDefault="qualified"
  4:     xmlns="http://tempuri.org/XMLSchema.xsd"
  5:     xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
  6:     xmlns:xs="http://www.w3.org/2001/XMLSchema"
  7: 	xmlns:a="http://www.bochenlin.com/author"
  8: >
  9: 	<xs:import namespace ="http://www.bochenlin.com/author"
 10: 			   schemaLocation ="author.xsd"></xs:import>
 11: 	<xs:element name="book">
 12: 		<xs:complexType>
 13: 			<xs:sequence>
 14: 				<xs:element name ="title" type="xs:string" ></xs:element>
 15: 				<xs:element name ="subject" type ="xs:string"></xs:element>
 16: 				<xs:element ref ="a:author" ></xs:element>
 17: 			</xs:sequence>
 18: 		</xs:complexType>
 19: 	</xs:element> 		
 20: </xs:schema>
 21: 

Author.xsd:


 



  1: <?xml version="1.0" encoding="utf-8"?>
  2: <xs:schema targetNamespace="http://www.bochenlin.com/author"
  3:     elementFormDefault="qualified"
  4:     xmlns="http://tempuri.org/XMLSchema.xsd"
  5:     xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
  6:     xmlns:xs="http://www.w3.org/2001/XMLSchema"
  7: >
  8: 	<xs:element name ="author">
  9: 		<xs:complexType>
 10: 			<xs:sequence >
 11: 				<xs:element name ="title" type ="xs:string"></xs:element>
 12: 				<xs:element name ="firstName" type ="xs:string"></xs:element>
 13: 				<xs:element name ="lastName" type ="xs:string"></xs:element>
 14: 			</xs:sequence>
 15: 		</xs:complexType>
 16: 	</xs:element>
 17: </xs:schema>
 18: 

 

By using the XML Data Binding Wizard in XE, we can get following code:

  1: unit book;
  2: 
  3: interface
  4: 
  5: uses xmldom, XMLDoc, XMLIntf;
  6: 
  7: type
  8: 
  9: { Forward Decls }
 10: 
 11:   IXMLBook = interface;
 12: 
 13: { IXMLBook }
 14: 
 15:   IXMLBook = interface(IXMLNode)
 16:     ['{C013C828-0F4C-4537-ACD3-B7C550D3B813}']
 17:     { Property Accessors }
 18:     function Get_Title: WideString;
 19:     function Get_Subject: WideString;
 20:     function Get_Author: WideString;
 21:     procedure Set_Title(Value: WideString);
 22:     procedure Set_Subject(Value: WideString);
 23:     procedure Set_Author(Value: WideString);
 24:     { Methods & Properties }
 25:     property Title: WideString read Get_Title write Set_Title;
 26:     property Subject: WideString read Get_Subject write Set_Subject;
 27:     property Author: WideString read Get_Author write Set_Author;
 28:   end;
 29: 
 30: { Forward Decls }
 31: 
 32:   TXMLBook = class;
 33: 
 34: { TXMLBook }
 35: 
 36:   TXMLBook = class(TXMLNode, IXMLBook)
 37:   protected
 38:     { IXMLBook }
 39:     function Get_Title: WideString;
 40:     function Get_Subject: WideString;
 41:     function Get_Author: WideString;
 42:     procedure Set_Title(Value: WideString);
 43:     procedure Set_Subject(Value: WideString);
 44:     procedure Set_Author(Value: WideString);
 45:   end;
 46: 
 47: { Global Functions }
 48: 
 49: function Getbook(Doc: IXMLDocument): IXMLBook;
 50: function Loadbook(const FileName: string): IXMLBook;
 51: function Newbook: IXMLBook;
 52: 
 53: const
 54:   TargetNamespace = 'http://www.bochenlin.com/book';
 55: 
 56: implementation
 57: 
 58: { Global Functions }
 59: 
 60: function Getbook(Doc: IXMLDocument): IXMLBook;
 61: begin
 62:   Result := Doc.GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
 63: end;
 64: 
 65: function Loadbook(const FileName: string): IXMLBook;
 66: begin
 67:   Result := LoadXMLDocument(FileName).GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
 68: end;
 69: 
 70: function Newbook: IXMLBook;
 71: begin
 72:   Result := NewXMLDocument.GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
 73: end;
 74: 
 75: { TXMLBook }
 76: 
 77: function TXMLBook.Get_Title: WideString;
 78: begin
 79:   Result := ChildNodes['title'].Text;
 80: end;
 81: 
 82: procedure TXMLBook.Set_Title(Value: WideString);
 83: begin
 84:   ChildNodes['title'].NodeValue := Value;
 85: end;
 86: 
 87: function TXMLBook.Get_Subject: WideString;
 88: begin
 89:   Result := ChildNodes['subject'].Text;
 90: end;
 91: 
 92: procedure TXMLBook.Set_Subject(Value: WideString);
 93: begin
 94:   ChildNodes['subject'].NodeValue := Value;
 95: end;
 96: 
 97: function TXMLBook.Get_Author: WideString;
 98: begin
 99:   Result := ChildNodes['a:author'].Text;
100: end;
101: 
102: procedure TXMLBook.Set_Author(Value: WideString);
103: begin
104:   ChildNodes['a:author'].NodeValue := Value;
105: end;
106: 
107: end.

In above Delphi code, there are two problems. First it does not generate correct code for element “author”. In line 27, type of property is WideString instead of a class type. Secondly, the generated Delphi code only specify the target name space http://www.bochenlin.com/book , no other name space http://www.bochenlin.com/author  is specified anywhere else, in stead, it uses fixed tag name “a:author” to map the element “author” in the name space http://www.bochenlin.com/author.  Even “author” is a simple type, this is only fine for XML instance which uses “a” as the prefix for name space http://www.bochenlin.com/author. If the final application will only deal with the XML coming from one source, it probably will be OK. It definitely has problem if the XML instances are coming from different sources in which they might have choose different prefixes for the same name space. Besides, the XML instance generated by using above code will be invalid as it will be missing the name space http://www.bochenlin.com/author for the element “author”. To solve these problems, schema “author” needs to have a small tweak.



Tweaked Author.xsd


  1: <?xml version="1.0" encoding="utf-8"?>
  2: <xs:schema targetNamespace="http://www.bochenlin.com/author"
  3:     elementFormDefault="qualified"
  4:     xmlns="http://tempuri.org/XMLSchema.xsd"
  5:     xmlns:mstns="http://tempuri.org/XMLSchema.xsd"
  6:     xmlns:xs="http://www.w3.org/2001/XMLSchema"
  7:     xmlns:a="http://www.bochenlin.com/author"		   
  8: >
  9: 	<xs:element name ="author">
 10: 		<xs:complexType>
 11: 			<xs:sequence >
 12: 				<xs:element name ="title" type ="xs:string"></xs:element>
 13: 				<xs:element name ="firstName" type ="xs:string"></xs:element>
 14: 				<xs:element name ="lastName" type ="xs:string"></xs:element>
 15: 			</xs:sequence>
 16: 		</xs:complexType>
 17: 	</xs:element>
 18: </xs:schema>
 19: 


Line 7 in above tweaked Author.xsd is added for Delphi to generated correct type for element “author”, updated unit book is provided below:

  1: unit book;
  2: 
  3: interface
  4: 
  5: uses xmldom, XMLDoc, XMLIntf;
  6: 
  7: type
  8: 
  9: { Forward Decls }
 10: 
 11:   IXMLBook = interface;
 12:   IXMLAuthor_a = interface;
 13: 
 14: { IXMLBook }
 15: 
 16:   IXMLBook = interface(IXMLNode)
 17:     ['{C873AF71-B66F-4FC1-8273-93F023EFAFBB}']
 18:     { Property Accessors }
 19:     function Get_Title: WideString;
 20:     function Get_Subject: WideString;
 21:     function Get_Author: IXMLAuthor_a;
 22:     procedure Set_Title(Value: WideString);
 23:     procedure Set_Subject(Value: WideString);
 24:     { Methods & Properties }
 25:     property Title: WideString read Get_Title write Set_Title;
 26:     property Subject: WideString read Get_Subject write Set_Subject;
 27:     property Author: IXMLAuthor_a read Get_Author;
 28:   end;
 29: 
 30: { IXMLAuthor_a }
 31: 
 32:   IXMLAuthor_a = interface(IXMLNode)
 33:     ['{23B0F904-2759-4810-9DDA-4E79248128E8}']
 34:     { Property Accessors }
 35:     function Get_Title: WideString;
 36:     function Get_FirstName: WideString;
 37:     function Get_LastName: WideString;
 38:     procedure Set_Title(Value: WideString);
 39:     procedure Set_FirstName(Value: WideString);
 40:     procedure Set_LastName(Value: WideString);
 41:     { Methods & Properties }
 42:     property Title: WideString read Get_Title write Set_Title;
 43:     property FirstName: WideString read Get_FirstName write Set_FirstName;
 44:     property LastName: WideString read Get_LastName write Set_LastName;
 45:   end;
 46: 
 47: { Forward Decls }
 48: 
 49:   TXMLBook = class;
 50:   TXMLAuthor_a = class;
 51: 
 52: { TXMLBook }
 53: 
 54:   TXMLBook = class(TXMLNode, IXMLBook)
 55:   protected
 56:     { IXMLBook }
 57:     function Get_Title: WideString;
 58:     function Get_Subject: WideString;
 59:     function Get_Author: IXMLAuthor_a;
 60:     procedure Set_Title(Value: WideString);
 61:     procedure Set_Subject(Value: WideString);
 62:   public
 63:     procedure AfterConstruction; override;
 64:   end;
 65: 
 66: { TXMLAuthor_a }
 67: 
 68:   TXMLAuthor_a = class(TXMLNode, IXMLAuthor_a)
 69:   protected
 70:     { IXMLAuthor_a }
 71:     function Get_Title: WideString;
 72:     function Get_FirstName: WideString;
 73:     function Get_LastName: WideString;
 74:     procedure Set_Title(Value: WideString);
 75:     procedure Set_FirstName(Value: WideString);
 76:     procedure Set_LastName(Value: WideString);
 77:   end;
 78: 
 79: { Global Functions }
 80: 
 81: function Getbook(Doc: IXMLDocument): IXMLBook;
 82: function Loadbook(const FileName: string): IXMLBook;
 83: function Newbook: IXMLBook;
 84: 
 85: const
 86:   TargetNamespace = 'http://www.bochenlin.com/book';
 87: 
 88: implementation
 89: 
 90: { Global Functions }
 91: 
 92: function Getbook(Doc: IXMLDocument): IXMLBook;
 93: begin
 94:   Result := Doc.GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
 95: end;
 96: 
 97: function Loadbook(const FileName: string): IXMLBook;
 98: begin
 99:   Result := LoadXMLDocument(FileName).GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
100: end;
101: 
102: function Newbook: IXMLBook;
103: begin
104:   Result := NewXMLDocument.GetDocBinding('book', TXMLBook, TargetNamespace) as IXMLBook;
105: end;
106: 
107: { TXMLBook }
108: 
109: procedure TXMLBook.AfterConstruction;
110: begin
111:   RegisterChildNode('author', TXMLAuthor_a);
112:   inherited;
113: end;
114: 
115: function TXMLBook.Get_Title: WideString;
116: begin
117:   Result := ChildNodes['title'].Text;
118: end;
119: 
120: procedure TXMLBook.Set_Title(Value: WideString);
121: begin
122:   ChildNodes['title'].NodeValue := Value;
123: end;
124: 
125: function TXMLBook.Get_Subject: WideString;
126: begin
127:   Result := ChildNodes['subject'].Text;
128: end;
129: 
130: procedure TXMLBook.Set_Subject(Value: WideString);
131: begin
132:   ChildNodes['subject'].NodeValue := Value;
133: end;
134: 
135: function TXMLBook.Get_Author: IXMLAuthor_a;
136: begin
137:   Result := ChildNodes['author'] as IXMLAuthor_a;
138: end;
139: 
140: { TXMLAuthor_a }
141: 
142: function TXMLAuthor_a.Get_Title: WideString;
143: begin
144:   Result := ChildNodes['title'].Text;
145: end;
146: 
147: procedure TXMLAuthor_a.Set_Title(Value: WideString);
148: begin
149:   ChildNodes['title'].NodeValue := Value;
150: end;
151: 
152: function TXMLAuthor_a.Get_FirstName: WideString;
153: begin
154:   Result := ChildNodes['firstName'].Text;
155: end;
156: 
157: procedure TXMLAuthor_a.Set_FirstName(Value: WideString);
158: begin
159:   ChildNodes['firstName'].NodeValue := Value;
160: end;
161: 
162: function TXMLAuthor_a.Get_LastName: WideString;
163: begin
164:   Result := ChildNodes['lastName'].Text;
165: end;
166: 
167: procedure TXMLAuthor_a.Set_LastName(Value: WideString);
168: begin
169:   ChildNodes['lastName'].NodeValue := Value;
170: end;
171: 
172: end.

Although above code has generated “author” as a class instead of a WideString, it has not put in its name space http://www.bochenlin.com/author at all. It can be solved by modify line 111 in above code:



111:   RegisterChildNode(‘author’, TXMLAuthor_a, ‘http://www.bochenlin.com/author’);


Yes, it is simple, but it is not perfect yet. If the schemas are as simple as these two in this blog and you are pretty sure they won’t going to be changed, congratulations, you have done the job. But if the number of involved schemas is large and they are likely to change in the near future, it means you are going to regenerate the code again and again so that you are going to loose the modifications and have to redo manually. Don’t know if you have noticed that it does not only loose the changes you have put in but also the GUIDs of the interfaces are changed every time the code is regenerated. So the maintenance of the code becomes very trivial and difficult. I have a better solution in terms of maintaining these auto-generated code. I will introduce Class Helpers to solved this problem in my next blog.