Debuggen, Tracen en Testen van Delphi Win32 Web Services

Debuggen, Tracen en Testen van Delphi Win32 Web Services

SOAP en Web Services zijn in mijn ogen een van de (mogelijke) bouwstenen voor een SOA. In dit artikel – dat ik schreef voor m’n SDE sessie van 1 juni - duik ik in de Delphi Win32 Web Service architectuur (beschikbaar vanaf Delphi 6), en laat ik zien hoe we Delphi Web Services kunnen testen, tracen en debuggen.

Debuggen kan o.a. met behulp van de Web App Debugger die echter vanaf Delphi 2005 niet meer zonder meer werkt (vanwege de nieuwere versie van Indy), maar die wel weer werkbaar te krijgen is.

Voor het tracen zal ik laten zien hoe we zowel binnenkomende SOAP requests als uitgaande SOAP responses kunnen "opvangen" en weergeven in een logfile (of debug window). Het is zelfs mogelijk om SOAP berichten op die manier aan te passen indien gewenst, en ik zal hier een voorbeeld (uit de praktijk!) van geven.

Voor het testen van Web Service "engines" kunnen we sinds Delphi 2005 gebruik maken van het ingebouwde Unit Testing framework. Met behulp van DUnit zal ik laten zien hoe we een Delphi SOAP Server op eenvoudige - en herbruikbare - wijze kunnen testen en valideren.

Delphi 2007 SOAP Server Application

Een groot deel van de code in dit artikel werkt met alle Win32 versies van Delphi sinds Delphi 6. Toch zal ik van de meest recente versie Delphi 2007 gebruik maken, en hier en daar melding maken van nieuwe features en mogelijkheden.

Allereerst wil ik een Win32 Web Service toepassing bouwen, en daarvoor kunnen we in Delphi gebruik maken van de SOAP Server Application wizard in de WebServices categorie van de Object Repository (zie figuur 1).

Fig. 1: New SOAP Server Application Icon

In de daarop volgende Wizard kunnen we kiezen voor ISAPI, CGI of de Web App Debugger Executable als web server applicatietype. Omdat ik wil demonstreren hoe we SOAP toepassingen kunnen tracen en debuggen, ligt de keuze voor de Web App Debugger Executable voor de hand. Specificeer een Class Name, zoals SDE (zie ook figuur 2).

Fig. 2: New SOAP Server Application Wizard

Er volgt nu een vraag of we een interface voor de SOAP module willen maken. Dat kunnen we ook later doen (met de SOAP Interface wizard uit de Object Repository), maar in beide gevallen krijgen we dezelfde dialoog (figuur 3 te zien.

Ik heb hier als Service de naam “MyService” ingevuld. De unit identifier wordt dan automatisch ook op MyService gezet, wat leidt tot een MyServiceIntf.pas unit voor de interface definitie en een MyServiceImpl.pas unit voor de implementatie, zoals we zo zullen zien. Commentaar en voorbeeld methodes zijn altijd handig, want die kunnen we makkelijk uitbreiden met onze eigen wensen.

Fig. 3: SOAP Server Interface Wizard

Voor we verder gaan wil ik eerst de bestanden van het project wat zinvolle(re) namen geven. Behalve de gegenereerde MyServiceIntf.pas en MyServiceImpl.pas bevat ons project nu namelijk ook een Unit1.pas en Unit2.pas. Unit1.pas is een leeg form dat gebruikt wordt als “main window” van onze toepassing als Web App Debugger client. Ik bewaar dat meestal in WADForm.pas.

Unit2.pas bevat de web module, met daarop al een drietal componenten: HTTPSoapDispatcher, HTTPSoapPascalInvoker en WSDLHTMLPublish. Deze unit bewaar ik onder de naam SWebMod.pas.

Het project zelf bewaar ik als SDESoapServerDemo.dpr.

WAD en Indy

Voordat we verdere wijzigingen gaan aanbrengen aan onze SOAP Server, moeten we eerst even (proberen te) compileren. Met Delphi 6 of 7 gaat dat zonder problemen, maar vanaf Delphi 2005 krijg je hier een foutmelding: Unit SockApp was compiled with a different version of IdTCPServer.TIdTCPServer.

Web App Debugger units zijn nog met Indy 9 gebouwd

Overigens is het mogelijk dat gebruikers van Delphi 2005 en 2006 deze foutmelding niet krijgen, maar dan alleen als zij bij de installatie van Delphi gekozen hebben om Indy 9 in plaats van Indy 10 te installeren. Het probleem wordt namelijk veroorzaakt doordat de Web App Debugger units in de tijd van Delphi 7 met Indy 9 zijn gebouwd. En helaas is Indy versie 10 een “breaking release”, waardoor de Web App Debugger het niet meer doet onder Indy 10. Er zijn aanpassingen mogelijk om de Web App Debugger ook met Indy 10 te laten werken, maar dan moet je zelf aanpassingen in de source code gaan maken (zie ook de readme bij Delphi 2007 voor details). Een snellere oplossing is om Delphi te instrueren om niet Indy 10 maar Indy 9 te gebruiken voor dit ene project. Het is namelijk gelukkig nog wel zo dat Indy 9 naast Indy 10 in je Delphi directory wordt geïnstalleerd (overigens ook bij Delphi 2007, ondanks het feit dat deze niet langer vraagt of je Indy 9 of Indy 10 wilt hebben, maar standaard gewoon Indy 10 neerzet).

In alle gevallen moeten we de Project Options dialog openen, en dan in de Directories/Conditionals pagina het Search path aanpassen, zodat $(BDS)\Lib\Indy9 in het path staat.

Fig. 4: Project Options – Search Path

Merk op dat $(BDS) nog steeds gebruikt wordt ook al heet Delphi 2007 officieel de CodeGear RAD Studio en niet meer de Borland Developer Studio.

Met deze kleine aanpassing compileert het project weer, en kunnen we de SDESoapServerDemo als Web App Debugger client draaien. Dit levert een leeg form op – op zich niet zo heel erg nuttig, maar achter de schermen is ook het MyService SOAP object te benaderen. En te debuggen.

Dit kunnen we demonstreren door uit het Tools menu in Delphi, de Web App Debugger zelf te starten. Dit is de server die alle requests doorsluist naar onze Web App Debugger client. Hierdoor kunnen we breakpoints zetten in het SDESoapServerDemo project en de Delphi debugger gebruiken indien noodzakelijk.

Nog een opmerking over de Web App Debugger: in de SWebMod.pas unit is een extra regel code opgenomen in de initialization sectie die de WebModuleClass van de WebRequestHandler een waarde geeft. Dit is alleen zinnig bij een Web App Debugger client, en deze regel code moeten we dan ook niet vergeten te verwijderen als we de SWebMod unit later in een CGI of ISAPI SOAP Server applicatie hergebruiken.

Een leuke truck hiervoor is gebaseerd op het feit dat een Web App Debugger client een zgn. GUI toepassing is, maar een CGI of ISAPI web server toepassing niet. En tijdens runtime kunnen we met de IsConsole variabele bekijken of we een console of GUI toepassing zijn. Oftewel, de code in de initialization sectie van de SWebMod.pas kunnen we als volgt aanpassen:

initialization
  if not IsConsole then
    WebRequestHandler.WebModuleClass := TWebModule2;
end.

Win32 SOAP Servers Tracen

Met een Web App Debugger client kunnen we breakpoints zetten in de Win32 SOAP Web Service. Dat is nuttig, maar vaak is het nuttiger om te zien welke SOAP berichten er heen en weer worden gestuurd. Daar kun je speciale tools voor inzetten, die het berichtenverkeer tussen twee of meer toepassingen gaan besnuffelen, maar het is niet al te moeilijk om dat in je eigen Win32 SOAP Server toepassing te doen (dat biedt je straks meteen de kans om de binnenkomende of uitgaande SOAP berichten hier en daar nog een beetje aan te passen indien nodig).

Als we even teruggaan naar de SOAP Web Module in SWebMod.pas, dan zien we daar de drie componenten van het Delphi Win32 SOAP Framework. De HTTPSoapDispatcher gebruikt de HTTPSoapPascalInvoker om het binnenkomende SOAP bericht te verwerken en een SOAP antwoord te genereren. De HTTPSoapPascalInvoker heeft een drietal events die we kunnen gebruiken om in dit proces mee te kijken (en eventueel in te grijpen).

Het OnBeforeDispatchEvent event zal worden gevuurd als het bericht is ontvangen maar nog voordat de bijbehorende methode van het gewenste SOAP object wordt uitgevoerd. Het event heeft een tweetal parameters: MethodName en Request.

procedure
  TWebModule2.HTTPSoapPascalInvoker1BeforeDispatchEvent(
  const MethodName: string; const Request: TStream);
var
  StrStream: TStringList;
begin
  StrStream := TStringList.Create;
  try
    Request.Position := 0;
    StrStream.LoadFromStream(Request);
    if not IsConsole then // Web App Debugger client
      Log('Before Dispatch:', StrStream.Text)
  finally
    StrStream.Free;
    Request.Position := 0
  end
end
;

Merk op dat er ook een OnBeforeDispatchEvent2 event is, met meer argumenten voor wie ze nodig heeft:

procedure
 
 TWebModule2.HTTPSoapPascalInvoker1BeforeDispatchEvent2
    (
       const MethodName: string;
       const Request: TStream;
       Response: TStream;
       var BindingType: TWebServiceBindingType;
       var Handled: Boolean);
begin

end;

De OnBeforeDispatchEvent2 kunnen we gebruiken in situaties waarin we zelf onze SOAP afhandeling willen implementeren (vandaar de Handled: Boolean parameter). Dat is niet iets dat ik in dit artikel wil doen, maar wel nuttig om te weten.

Na afloop van het uitvoeren van de SOAP method wordt het antwoord samengesteld, en kunnen we in de OnAfterDispatchEvent kijken wat er in het response staat.

procedure
 
 TWebModule2.HTTPSoapPascalInvoker1AfterDispatchEvent(
  const MethodName: string; SOAPResponse: TStream);
var
  StrStream: TStringList;
begin
  StrStream := TStringList.Create;
  try
    SOAPResponse.Position := 0;
    StrStream.LoadFromStream(SOAPResponse);
    if not IsConsole then // Web App Debugger client
      Log('After Dispatch:',StrStream.Text)
  finally
    StrStream.Free;
    SOAPResponse.Position := 0
  end
end
;

Win32 Echo Server

Tijd om wat code aan onze eigen MyService toe te voegen. Als we voor de example methods hebben gekozen, krijgen we al enkele “echo” methoden. Die zijn uitermate geschikt om de interoperabiliteit tussen onze Win32 SOAP Server en andere talen en/of platforms te testen.

Om een aantal kleine problemen met interoperabiliteit tussen Win32 en .NET te demonstreren moeten we een paar kleine uitbreidingen doen aan de MyServiceIntf.pas en MyServiceImpl.pas units.

In MyServiceIntf.pas wil ik de TMyEmployee uitbreiden met een Birthdate veld van type TXSDateTime. De definitie komt er hierdoor als volgt uit te zien:

  TMyEmployee = class(TRemotable)
  private
    FLastName: AnsiString;
    FFirstName: AnsiString;
    FSalary: Double;
    FBirthdate: TXSDateTime;
  published
    property LastName: AnsiString
      read FLastName write FLastName;
    property FirstName: AnsiString
      read FFirstName write FFirstName;
    property Salary: Double
      read FSalary write FSalary;
    property Birthdate: TXSDateTime
      read FBirthdate write FBirthdate;
  end;

Daarnaast wil ik de verzameling echoXXX methods van de IMyService uitbreiden met een echoDateTime, zodat het IMyService interface er als volg uit komt te zien:

{ Invokable interfaces must derive from IInvokable }
IMyService = interface(IInvokable)
['{721D51D6-BEA6-4D00-A152-507D7CC42F86}']

  { Methods of Invokable interface must not use
    the default }
  { calling convention; stdcall is recommended }
  function echoEnum(const Value: TEnumTest):
    TEnumTest; stdcall;
  function echoDoubleArray(const Value:TDoubleArray):
    TDoubleArray; stdcall;
  function echoMyEmployee(const Value: TMyEmployee):
    TMyEmployee; stdcall;
  function echoDouble(const Value: Double):
    Double; stdcall;
  function echoDateTime(const Value: TXSDateTime):
    TXSDateTime; stdcall;
end;

In de MyServiceImpl.pas moeten we nu de definitie van TMyService aanpassen, en de implementatie van de echoMyEmployee en echoDateTime completeren. De twee methods zijn als volgt geïmplementeerd:

function TMyService.echoMyEmployee
  (const Value: TMyEmployee): TMyEmployee; stdcall;
begin
  Result := TMyEmployee.Create;
  Result.LastName := Value.LastName;
  Result.FirstName := Value.FirstName;
  Result.Salary := Value.Salary;
  Result.Birthdate := Value.Birthdate.Clone
end;

function TMyService.echoDateTime(
  const Value: TXSDateTime): TXSDateTime;
  stdcall;
begin
  Result := Value.Clone
end;

Een Delphi Win32 client aan deze Delphi Win32 server koppelen heeft weinig zin, want dan zal alles naar wens werken. Het is juist spannend om er eens een andere client aan te hangen, zoals een C# of Delphi for .NET client.

C# zal ik straks nog even laten zien (bij een .NET 2.0 server), dus gaan we nu nog even verder met Delphi for .NET.

Delphi for .NET Client

Start een nieuwe Delphi for .NET toepassing (dit kan nog niet met Delphi 2007, dus hier zullen we Delphi 2006 voor moeten gebruiken bijvoorbeeld), zoals een VCL for .NET toepassing. Via Add Web Reference in the Project Manager kun je een link naar de WSDL van een SOAP Server laten importeren. Voor onze Win32 Web Service is deze URL http://localhost:8081/SDESoapServerDemo.SDE/wsdl/IMyService en die kunnen we invoeren in de Add Web Reference dialoog:

Fig. 5: Add Web Reference dialoog

Nadat de web reference is toegevoegd aan ons project, kunnen we de gegenereerde unit gebruiken om een instantie van de IMyServiceservice Web Servce te maken. Er zijn twee dingen die ik wil testen (de rest gaat gewoon goed): echoDouble en echoMyEmployee, en van de laatste dan vooral het Birthdate veld.

Beginnend met de echoDouble, kan ik de volgende code gebruiken om te testen of wat er in gaat ook gelijk is aan wat eruit komt. Met “normale” floating point getallen gaat dit goed, maar met een breuk zoals 42/9 komen we een beetje in de problemen.

procedure TForm2.btnEchoDoubleClick(Sender: TObject);
const
  Value = 42/9;
var
  WS: IMyServiceservice;
  X: Double;
begin
  WS := IMyServiceservice.Create;
  X := WS.echoDouble(Value);
  if X <> Value then ShowMessage('Niet OK')
end;

De messagebox met “Niet OK” wordt vertoond als we 42/9 proberen te echo’en. Als we hier willen zien wat er mis is, moeten we in de trace kijken en het binnenkomende SOAP request vergelijken met de uitgaande SOAP response. Ik heb alles even weggehaald behalve de values, en dan zien we dat er het volgende in komt:

13:59:39.869 Before Dispatch:

13:59:39.869 4.666666666666667

En het volgende als echo antwoord weer uitgaat:

13:59:39.879 After Dispatch:

13:59:39.879 4.66666666666667

Het is moeilijk te zien, maar in het request staan er 14 6-jes voor de 7, en in de response staan er maar 13 6-jes. Het probleem is niet echt een probleem, maar wordt dus veroorzaakt door het feit dat er op verschillende manieren van een double waarde naar een SOAP XML string wordt ge-marshalled. In .NET is een Double een type dat kennelijk 16 significante cijfers heeft. Voor Win32 geeft de online help aan dat een Double 15-16 significante decimalen heeft; 15 dus in ons voorbeeld.

Het gekke is bovendien dat als we het verschil tussen X en Value willen laten zien, dat we dan 0 krijgen. In praktijk zal het probleem tussen 15 of 16 significante decimalen nauwelijks een echt issue zijn, maar dat is mijn persoonlijke mening.

Interessanter is de test van de TXSDateTime data – zowel als losse parameter en als veld van de TMyEmployee class.

procedure TForm2.btnechoMyEmpClick(Sender: TObject);
var
  WS: IMyServiceservice;
  Emp, Emp2: TMyEmployee;

begin
  WS := IMyServiceservice.Create;
  Emp := TMyEmployee.Create;
  Emp.FirstName := 'Bob';
  Emp.LastName := 'Swart';
  Emp.Salary := 42;
  Emp.Birthdate := DateTime.Now;
  Emp2 := WS.echoMyEmployee(Emp);
  if Emp2.FirstName <> Emp.FirstName then
  
 ShowMessage('FirstName differs');
  if Emp2.LastName <> Emp.LastName then
  
 ShowMessage('LastName differs');
  if Emp2.Salary <> Emp.Salary then
  
 ShowMessage('Salary differs');
  if Emp2.Birthdate.Date <> Emp.Birthdate.Date then
    ShowMessage(Emp.Birthdate.ToString + ' <> ' +
      Emp2.Birthdate.ToString)
  else
    ShowMessage(Emp.Birthdate.ToString + ' = ' +
      Emp2.Birthdate.ToString);
  // now just echo the Birthdate value
  Emp2.Birthdate := WS.echoDateTime(Emp.Birthdate);
  if Emp2.Birthdate.Date <> Emp.Birthdate.Date then
    ShowMessage(Emp.Birthdate.ToString + ' <> ' +
      Emp2.Birthdate.ToString)
end;

Deze interoperability test gaat goed als de Win32 SOAP Server is gecompileerd met Delphi 2007, maar bij oudere versies van Delphi krijgen we een foutmelding, en is het Birthdate datum veld dat we terugkrijgen in de MyEmployee helemaal leeg!

Om dat te illustreren kunnen we het SDESoapServerDemo project eens sluiten in Delphi 2007 en openen in Delphi 2006 om het daar opnieuw te compileren en te runnen in de Web App Debugger.

We krijgen dan een foutmelding, die wordt veroorzaakt ergens diep in de VCL SOAP source code.

Het logfile laat het volgende zien:

2007-05-31T13:59:10.3170672+02:00

Delphi 2007 for Win32 geeft het volgende – goede – antwoord:

2007-05-31T14:25:23.9798832+02:00

Maar Delphi 6 t/m 2006 geven het volgende – foute – antwoord:

2007-05-31T13:59:10.3170672+02:00

Het verschil zit hem in de xsd: prefix van de Birthdate. Met deze prefix erbij gaat het fout, en zonder de prefix gaat het goed. Dit kunnen we op twee manieren oplossen. Enerzijds kunnen we diep in de VCL SOAP units kijken waar het probleem optreedt. Om een lange zoektocht en bijbehorend verhaal kort te maken: dat blijkt regel 2765 van de unit OPToSOAPDomConv.pas te zijn. Hier zouden we de lege [] moeten veranderen in [ocoDontPrefixNode] om de prefix te onderdrukken en een goed gedrag te krijgen in Delphi 2006. Merk op dat het in Delphi 2007 meteen al goed werkt, dus daar zijn geen verdere aanpassingen nodig.

Een alternatieve manier voor Delphi 2006 bestaat uit het – achteraf – aanpassen van de SOAP response in de OnAfterDispatchEvent, als volgt:

procedure
 
TWebModule2.HTTPSoapPascalInvoker1AfterDispatchEvent(
    const MethodName: string; SOAPResponse: TStream);
var
  StrStream: TStringList;
  Str: String;
  Modified: Boolean;

begin
  StrStream := TStringList.Create;
  try
    SOAPResponse.Position := 0;
    StrStream.LoadFromStream(SOAPResponse);
    Str := StrStream.Text;
    if not IsConsole then Log('After Dispatch:',Str);

    Modified := False;
    while Pos('xsd:Birthdate',Str) > 0 do
    begin
      Modified := True;
      Delete(Str,Pos('xsd:Birthdate',Str),4)
    end;
    if (not IsConsole) and Modified then
    
 Log('Modified SOAP Response:',Str);

    SOAPResponse.Size := 0;
    SOAPResponse.Position := 0;
    StrStream.Text := Str;
    StrStream.SaveToStream(SOAPResponse)

  finally
    StrStream.Free;
    SOAPResponse.Position := 0
  end
end
;

Deze aanpassing werkt voor Delphi 2006, en is natuurlijk geheel voor eigen risico, maar het laat wel zien hoe we de SOAPResponse nog net kunnen aanpassen voor de Win32 SOAP Web Service het antwoord terugstuurt naar de client.

We kunnen de SOAPResponse aanpassen voor de Win32 SOAP Web Service het terugstuurt

.NET SOAP Server

Nu we hebben gezien dat er soms wat mis kan gaan tussen een Win32 SOAP Server en een .NET SOAP Client, is het tijd om het verhaal om te draaien. Als kleine demonstratie heb ik een C# Web Service geschreven in EchoNET2.asmx, die we onder ASP.NET 2.0 kunnen laten draaien.

<%@ WebService Language="C#" Class="ASPNET2WS.Echo" %>

using System;
using System.Web.Services;

namespace ASPNET2WS
{
  [WebService(Namespace="http://eBob42.org")]
  public class Echo: System.Web.Services.WebService
  {
    [WebMethod(Description=
       "Echo a value of type double")]
    public double echoDouble(double doubleValue)
    {
      return doubleValue;
    }

    [WebMethod(Description=
      "Echo a value of type DateTime")]
    public DateTime echoDateTime(DateTime DateTimeValue)
    {
      return DateTimeValue;
    }

    public struct MyEmployee
    { 
      public String FirstName;
      public DateTime BirthDate;
    }
    [WebMethod(Description=
      "Echo a value of type MyEmployee")]
    public MyEmployee EchoMyEmployee(MyEmployee emp)
    {
      return emp;
    }
  }
}

Dit voorbeeld is op het internet te vinden als http://www.ebob42.com/cgi-bin/EchoNET2.asmx?WSDL maar kan ook lokaal getest worden. In dat geval kunnen we de URL http://localhost/cgi-bin/EchoNET2.asmx?WSDL in Delphi importeren met de WSDL Importer.

Fig. 6: WSDL Importer dialoog

Zowel met Delphi 2006 als Delphi 2007 kunnen we dan een client toepassing schrijven:

procedure TForm2.btnEchoClick(Sender: TObject);
var
  WS: EchoSoap;
  Emp, Emp2: MyEmployee;
begin
  WS := GetEchoSoap;
  Emp := MyEmployee.Create;
  Emp.Birthdate := TXSDateTime.Create;
  Emp.FirstName := 'Robert';
  Emp.Birthdate.AsDateTime := Now;
  Emp2 := WS.echoMyEmployee(Emp);
  ShowMessage(DateTimeToStr(Emp2.Birthdate.AsDateTime));
  Emp2.Birthdate := WS.echoDateTime(Emp.Birthdate);
  ShowMessage(DateTimeToStr(Emp2.Birthdate.AsDateTime))
end;

Dit zal laten zien dat het in Delphi 2007 wel werkt, maar voor eerdere versies van Delphi niet. Overigens alleen als de C# Web Service onder ASP.NET 2.0 draait. Bij het configureren van de virtual directory als een ASP.NET 1.1 Web Service draait het zonder problemen. De oorzaak van het interoperability probleem moet dan ook gezocht worden in de verschillen tussen ASP.NET 1.1 en ASP.NET 2.0, en blijken het gevolg te zijn van het op een andere manier aangeven van de document|literal binding die ASP.NET web services gebruiken. De Delphi WSDL Importer – met uitzondering van die van Delphi 2007 – is niet in staat om de nieuwe manier waarop dit wordt aangegeven te herkennen. Met als gevolg dat een cruciale declaratie achterwege wordt gelaten: het feit dat het SOAP interface als een zgn. ioDocument interface geregistreerd met worden.

Het interoperability probleem moet gezocht worden in de verschillen tussen ASP.NET 1.1 en ASP.NET 2.0

In praktijk is dat makkelijk op te lossen, nl. door één regel code aan het eind van de SOAP import unit toe te voegen. In ons voorbeeld is die regel als volgt:

InvRegistry.RegisterInvokeOptions
  (TypeInfo(EchoSoap), ioDocument);

En daarmee draait het ook onder oudere versies van Delphi weer als een trein.

HTTPRio SOAP Clients Tracen

Los van deze snelle oplossing is het soms toch handig om te zien wat er achter de schermen gebeurt. Om bij een Delphi Win32 SOAP Client bij te houden wat er in komt er en wat er uit gaat, moeten we een eigen instantie van de HTTPRio maken en daar enige events van invullen. We hebben zowel een OnBeforeExecute als een OnAfterExecute event waar we iets kunnen doen. Om het vorige Log voorbeeld te herhalen, zou dat als volgt kunnen gaan:

procedure TForm7.HTTPRIO1BeforeExecute(
  const MethodName: string;
  var SOAPRequest: WideString);
begin
  Log('DoBeforeRequest:',SOAPRequest)
end;

procedure TForm7.HTTPRIO1AfterExecute(
  const MethodName: string;
  SOAPResponse: TStream);
var
  StrList: TStringList;
begin
  StrList := TStringList.Create;
  try
    SOAPResponse.Position := 0;
    StrList.LoadFromStream(SOAPResponse);
    Log('DoBeforeRequest:',StrList.Text)
  finally
    SOAPResponse.Position := 0;
    StrList.Free
  end
end
;

Het enige jammere is dat in Delphi 2006 (en eerder) de SOAPRequest WideString parameter van de OnBeforeExecute helemaal niet gewijzigd kan worden. Het kan wel, maar het heeft geen effect. Met ingang van Delphi 2007 is dat weer hersteld, en kunnen we vrolijk wijzigingen maken in de SOAPRequest – bijvoorbeeld de ‘o’ in Robert veranderen in een ‘u’ om zo tot Rubert te komen.

procedure TForm2.HTTPRIO1BeforeExecute(
  const MethodName: string;
  var SOAPRequest: WideString);
begin
  while Pos('Robert', SOAPRequest) > 0 do
  begin
    SOAPRequest[Pos('Robert', SOAPREquest)+1] := 'u'
  end;
  ShowMessage(SOAPRequest)
end;

Dit werkt dus pas in Delphi 2007 (en niet in eerdere versies). Om het ook in oudere versies van Delphi te laten werken heb ik een eigen THTTPRio class gemaakt die als volgt geïmplementeerd is, en zelf het SOAPRequest aanpast nadat de OnBeforeExecute event handler is aangeroepen. De source code van deze nieuwe class – handig voor wie nog Delphi 2006 gebruikt bijvoorbeeld – is als volgt:

type
  THTTPRIO2 = class(THTTPRIO)
    constructor Create(Owner: TComponent); override;
    procedure BeforeExecuteHandler(
      const MethodName: String;
      var SOAPRequest: WideString);
    procedure AfterExecuteHandler(
      const MethodName: String;
      SOAPResponse: TStream);
  protected
    procedure DoBeforeExecute(const MethodName: string;
       Request: TStream); override;
  end;

{ THTTPRIO2 }

constructor THTTPRIO2.Create(Owner: TComponent);
begin
  inherited;
  OnBeforeExecute := BeforeExecuteHandler;
  OnAfterExecute := AfterExecuteHandler
end;

procedure THTTPRIO2.DoBeforeExecute(
  const MethodName: string;
  Request: TStream);
var
  StrList: TStringList;
  Str: WideString;
begin
  if Assigned(OnBeforeExecute) then
  begin
    StrList := TStringList.Create;
    try
      Request.Position := 0;
      StrList.LoadFromStream(Request);
      Str := StrList.Text;
      OnBeforeExecute(MethodName, Str);
      Log('DoBeforeRequest:',Str);
      Request.Size := 0;
      Request.Position := 0;
      StrList.Text := Str;
      StrList.SaveToStream(Request)
    finally
      Request.Position := 0;
      StrList.Free
    end
  end
end
;

procedure THTTPRIO2.BeforeExecuteHandler(
  const MethodName: String;
  var SOAPRequest: WideString);
begin
  Log('Before Execute:', SOAPRequest);
end;

procedure THTTPRIO2.AfterExecuteHandler(
  const MethodName: String;
  SOAPResponse: TStream);
var
  StrStream: TStringList;
  Str: String;
begin
  StrStream := TStringList.Create;
  try
    SOAPResponse.Position := 0;
    StrStream.LoadFromStream(SOAPResponse);
    Str := StrStream.Text;
    Log('After Execute:', Str);
    SOAPResponse.Position := 0;
    StrStream.Text := Str;
    StrStream.SaveToStream(SOAPResponse)
  finally
    StrStream.Free;
    SOAPResponse.Position := 0
  end
end
;

Wie meer informatie of ondersteuning zou willen hebben bij de toepassing van deze nieuwe THTTPRIO2 class kan me natuurlijk altijd een e-mailtje sturen.

SOAP Interfaces Testen met DUnit

Nu we het tracen van SOAP en het debuggen van de SOAP server en client toepassingen zelf hebben gezien, blijft er nog één onderwerp over: het daadwerkelijk testen van de SOAP methods met behulp van DUnit. Waar we in de voorgaande voorbeelden o.a. de SOAP berichten heen en weer bekeken om te zien wat er precies over de lijn ging, en waar het mogelijk fout ging, daar hebben we soms behoefte aan het testen van de methods van het SOAP object zelf, zonder daarbij de SOAP laag zelf te gebruiken, ofwel het lokaal aanroepen en testen van de methods als een soort van droogzwem-simulatie om te zien of de onderliggende berekeningen en implementaties van de methods goed zijn. Bij echoXXX methods is dat natuurlijk minder zinvol (de kans is niet zo groot dat een echo niet werkt), maar vooral bij ingewikkeldere methods kan het zinvol zijn om daar een aantal tests van de implementeren en bij de hand te houden. DUnit kan daarbij helpen.

Vanuit de Delphi IDE kunnen we binnen de projectgroep van het bestaande project een nieuw project toevoegen, en daarbij moeten we dan kiezen voor een Test Project. In de Test Project Wizard die dan volgt zal als project automatisch de naam van het actieve project worden ingevuld (SDESoapServerDemo) met “Tests” erachter. Als locatie wordt voorgesteld om een Test subdirectory te maken onder de huidige project directory (dat werkt erg prettig, want hierdoor staat het test project los van het daadwerkelijke project, maar kan het toch eenvoudig van alle source files gebruik maken door .. in het Search Path op te nemen).

Tot slot is het belangrijk om als persoonlijkheid “Delphi” op te geven (en niet bijvoorbeeld Delphi for .NET), en het test project toe te voegen aan de project groep, zodat er eenvoudig heen-en-weer geswitcht kan worden.

Fig. 7: Test Project Wizard

Het test project is nog leeg, maar daar kunnen we nieuwe test cases aan toevoegen. In de Unit Test categorie van de Object Repository vinden hiervoor het betreffende icon dat voor ons de Test Case Wizard start. Hier kunnen we uit de project direcotry units selecteren waar de source code van gescand zal worden, op zoek naar de public methods van classes. In het geval van onze SDESoapServerDemo is er eigenlijk maar één unit die relevant is, en dat is de MyServiceImpl.pas unit, want die bevat als enige het TMyService object en de bijbehorende echoXXX methods. De MyServiceIntf.pas unit bevat alleen de interface definitie, maar niet de implementatie (en die willen we juist testen!).

Kies dus voor de Impl.pas unit in de Test Case Wizard, en selecteer vervolgens alle SOAP classes en binnen deze classes de methods die we willen testen (zie figuur 8).

Fig. 8: Test Case Wizard

Er zal nu een speciale TestMyServiceImpl.pas unit gegenereerd worden, met daarin een TestTMyService class die behalve een Setup en TearDown method ook voor iedere geselecteerde methode uit de TMyService class (zie figuur 8) een test methode heeft toegevoegd aan de TestTMyService class.

In ons geval, leidt dat tot de volgende definitie:

type
  // Test methods for class TMyService
  TestTMyService = class(TTestCase)
  strict private
    FMyService: TMyService;
  public
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestechoEnum;
    procedure TestechoDoubleArray;
    procedure TestechoMyEmployee;
    procedure TestechoDouble;
    procedure TestechoDateTime;
  end;

In de Setup wordt al een instantie van TMyService aangemaakt, en die wordt in de TearDown weer vrijgegeven. In de verschillende TestechoXXX methods kunnen we gebruikmaken van deze instantie om de echo methode te testen zonder daarbij “gestoord” te worden door de SOAP laag eromheen.

Voor de TestechoMyEmplyee kan de implementatie er bijvoorbeeld als volgt uit komen te zien (waarbij we de waarde van Birthdate testen):

procedure TestTMyService.TestechoMyEmployee;
var
  ReturnValue: TMyEmployee;
  Value: TMyEmployee;
begin
  Value := TMyEmployee.Create;
  Value.Birthdate := TXSDateTime.Create; // anders AV
  try
    Value.FirstName := 'Bob';
    ReturnValue := FMyService.echoMyEmployee(Value);
    CheckEqualsString(Value.FirstName,
      ReturnValue.FirstName, 'FirstName')
  finally
    Value.Birthdate.Free;
    Value.Free
  end
end
;

Let op dat de verschillende CheckXXX routines die beschikbaar zijn binnen DUnit Test Cases, als eerste argument het te verwachten antwoord hebben, en als tweede argument het daadwerkelijk berekende antwoord (op die manier wordt ook de foutmelding opgebouwd: expected XXX but received YYY).

Er is één probleem met het gebruik van DUnit in Delphi 2007, en dat is het feit dat Delphi 2007 niet de volledige source code van DUnit installeert op disk (wat wel zou moeten), maar wel de volledige verzameling .dcu files. Op zich geen probleem, ware het niet dat alle nieuwe DUnit projecten in Delphi 2007 in het Search Path de directory $(BDS)\Source\DUnit\src met de DUnit source code hebben staan. En dus krijg je een compiler error, dat de unit FastMMMemLeakMonitor niet gevonden kan worden. Dit is op te lossen door in de Project Options het Search Path aan te passen (en er alleen .. in te zetten, zie figuur 9).

Fig. 9: Delphi 2007 Project Options

Dit lost het probleem weer op. Helaas komt het probleem weer terug zodra je een nieuwe Test Case toevoegt (zie figuur 8), waardoor je steeds opnieuw het Search Path zal moeten aanpassen. Gelukkig is CodeGear op de hoogte van het probleem, en komt er binnenkort een fix (of misschien is die er inmiddels al), waarbij de volledige source code van DUnit zit, waaronder ook de FastMMMemLeakMonitor unit dus.

We hoeven de tests dus maar één keer te schrijven, maar kunnen ze altijd blijven gebruiken

Tijdens de SDE sessie op vrijdag 1 juni kreeg ik overigens nog een mooie Access Violation (die de mensen in de zaal zich vast nog kunnen herinneren), die veroorzaakt werd door het feit dat ik in de TestechoMyEmployee method het Birthdate veld niet had gecreeerd. Dat lijkt niet zo’n probleem, maar als je de implementatie van de echoMyEmployee erbij haalt zie je dat ik de Clone method aanroep van de inkomende BIrthdate, en dat is natuurlijk geen goed idee als de Birthdate nil is.

Los daarvan levert unit testing een verzameling test routines die gecompileerd en uitgevoerd kunnen worden om daarmee de correctheid van de SOAP methods te verifiëren. En het mooie is dat we alleen het test project nog een keer hoeven te compileren en te runnen als de onderliggende code ooit mocht wijzigen. We hoeven de tests dus maar één keer te schrijven, maar kunnen ze tot in de verre toekomst blijven gebruiken.

Bij positief resultaat zien we allemaal groene lichtjes (zie figuur 10), en bij fouten verschijnen de details van de CheckXXX routine in beeld voor meer informatie.

Fig. 10: Unit Test dialoog

Een laatste gevolg van het op deze manier testen van SOAP methods is dat we nu ook zeker(der) zijn van het feit dat de signature van de SOAP methods niet zomaar mag wijzigen. Dat zou namelijk alle client toepassingen “in het wild” om zeep kunnen helpen, doordat die dan via SOAP requests versturen voor methods die niet meer in de oude vorm bestaan (te vergelijken met het wijzigen van de Windows API waardoor toepassingen niet meer draaien op nieuwe versies van Windows). Indien de SOAP methods zijn aangepast is dat meteen te merken als het test project niet meer compileert. Ik heb nl. één keer daadwerkelijk meegemaakt dat een “ontwikkelaar” een aantal SOAP methods grondig had gewijzigd terwijl er al verschillende client toepassingen in gebruik waren die afhankelijk waren van deze methods. Doodzonde nummer één in de Web Service wereld als je het mij vraagt, en Unit Testing helpt mij in ieder geval bij het opsporen ervan.

Conclusie

Tijdens de SDE sessie van 1 juni, en in dit artikel (voor wie er niet bij was – zie ik je de volgende keer weer?), heb ik laten zien hoe we Win32 Web Services kunnen debuggen, de SOAP berichten kunnen tracen, en de SOAP methods kunnen testen met behulp van Delphi (en het laatste ook met DUnit). Deze technieken zijn met name van belang bij het gebruik van SOAP in heterogene omgevingen, waarbij alleen de server (of de client) in Delphi for Win32 is geschreven, maar er aan de andere kant met andere omgevingen gewerkt wordt.

Wie nog vragen of opmerkingen heeft kan me altijd per e-mail bereiken of een bezoek brengen aan mijn nieuwe trainingsruimte in Helmond Brandevoort (zie www.eBob42.com voor details).

Referenties

Commentaar van anderen:
bags op 9-7-2010 om 9:48
Christian Dior for sale, there are more and croum for sale more online stores sell DeWitt for sale and false handbags wallet reproductions replica Burberry hangdbags. This means that you need to know replica Cartier hangdbagsthat you can trust, online retailers purchased copies of replica Veneta. The imitation replica Valentino handbags are listed above when selling counterfeit designer replica Concord.What can you replica Franck Muller expect in your handbag catalogue reproductions, including: Breguet replica. The great attention,replica Valentino handbags to ensure that your original copies, replica Versace handbags looks like. This includes the same materialAnya Hindmarch replica, sewing and correct an Balenciaga replica exact copy of the designer's logo.
replica handbags op 16-7-2010 om 9:50
If you have designer handbagsthe ability to choose thechanel replica right style from thousands of designer bags in the replica balenciagamarket to fit your needs and bottega veneta handbagsoccasions, you will be quiteswiss watches of the ordinary people and enjoyBurberry replica the great feeling they couldn’tGraham for sale imagine.The Instrumento NovantatreFerrari for sale offers the choice between a gold oris for saleand steel version, optionally Audemars Piguet for saledecorated with gemstones.replica handbags The model has joined both the ladies'Mulberry replica and gentlemen's de Grisogono bally handbagscollections. The curved sapphirereplica croum case back, featuring the corporate crestreplica Christian Dior, demonstrates the workings, the movement andreplica ebel its oscillating weight, both Cheap Handbags blackened by the PVD process. With a beautiful cheap fendi handbagson your shoulder, you could walk proudly in the hermes replica handbagsstreet and people will eye you with admirationburberry replica handbags.
ghdivstyler op 7-8-2010 om 9:06
http://www.google.com.hk/search?q=%22Geef+feedback%22+site:www.sdn.nl&hl=zh-CN&newwindow=1&safe=strict&ei=sARdTMy5BYu4uQOQ8pyaDA&start=240&sa=N
lkm op 12-8-2010 om 7:55
Researchers at Indian Wedding Dress CollectionWest Virginia University Wedding Dress RentalSchool of Medicine based their finding on data from 30,397 adults who participated Beautiful Wedding Dressesin the 2005 National HealthSilk Wedding DressInterview Survey, which collected information on demographic factors, socioeconomic wedding dresscharacteristics, lifestylewedding dress and health.
ChristianLouboutin op 16-8-2010 om 4:48
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Christian Louboutin comfortable shoes are women best resolution Whoever you, Drafted this think you can expect to take pleasure in Christian Louboutin Shoes, Wedding Shoes, Christian Louboutin, Christian Louboutin Shoes. This sneakers experience women charm additionally sexy. Wedding Shoes, Discount Christian Louboutin, Christian, Louboutin, Christian Louboutin Sale This is usually fantastic Louboutin Shoes, Louboutin Sale, Cheap Christian Louboutin, Christian Louboutin Discount, Christian Louboutin Boots. As a result exist to help opt designed for style, you cherish it is usually to help opt in order for most of the eye-catching Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Evening, Christian Louboutin Wedges taht can acquire inspiration designed for his fatal stiletto investigation connected with an incident that will occurred as part of his the beginning of the twenties. Christian Louboutin Pumps, Christian Louboutin Boots, Christian Louboutin Sandals, Christian Louboutin Flats, Manolo Blahnik Shoes He visited a museum and furthermore, saw a warning that will forbade women in order to really act, Yves Saint Laurent Shoes, Yves Saint Laurent Boots, YSL Shoes, Miu Miu Shoes during bearing stilettos ready, fearing damage in order to this extensive wood floors. Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress This image stayed in their head, along with he used this idea later in his louboutin shoes.
Administrator op 21-8-2010 om 10:00
There are some of the key methods available by which you can easily come under limelight in a party or any other special occasion. First of all Juicy Couture replicareplica Jimmy Choo handbagsdiscount designer bagscheap Louis Vuitton handbagsVersace handbagsreplica handbagscheap Christian Dior handbagsdesigner bagsVersace replica , it is imperative that you should not go with accessories that can provide odd look to your personality. There is no use of giving public appearances with outfits and accessories that do not match up with the current fashion trends. It is worth notable that you should take into consideration each and every feature right from tip to toe so that all the people should appreciate your unique presence. Choice that is associated with cheap Thomaswylde handbags is also not an omission to it. If you are shopping for one or more Mulberry replica handbags then make sure that they are prone to the existing trends of fashion. There are some kinds of thomaswylde replica available in the markets which have proven to be an evergreen choice for the ladies. One such option is to go for designer prada balenciaga replica. Prada Kooba handbags is no doubt considered to be a symbol of quality and durability. If you have limited budget with you then also you can make a better deal with the help of a dignified company. If you are getting the hermes replica handbags offered by a reliable company then you can look for the replica prada balenciaga replica handbags available there.? It is not necessary that you should wait for genuine prada Valentino handbags. You can fulfill your requirements at cheap prices with replica prada cheap Chloe handbags. They can also offer you best feature and services but only if you are getting it from a competent company.?? To prevent a pitfall while dealing it is necessary to avoid a straightforward approach. There are lots of sites available online that offer prada coach replica but fail to provide later what they actually claim for in advance. Even while going for replica prada Marni handbags customers fail to get the same that they truly deserve. Like most of the smart customers, if you are also shopping for prada dolce&Gabbana handbags online then chances are high that you can make a better use of your customer right. In addition to it, it is fast and convenient as well. You can make a better deal by sitting at your home in just few clicks. If you are surfing the internet then you can take the help of search engines. With some of the precise keywords like ‘Jimmy Choo handbags’ you can avail better results in no time. While looking for all the leading sites offering prada replica Chanel handbags don’t forget the check-out the site named as balenciaga handbags. It is one of the top sites existing online offering a great variety in cheap Coach handbags. The best part of looking into the prada coach handbags offered by louis Vuitton replica handbags is that you can bring home the genuine masterpiece of craftsmanship. cheap Versace handbags demands nothing more than the competitive prices prevailing in the market. There is no question of getting cheated over here. In the market, this site is considered to be the symbol of quality and reliability. cheap Mulberry handbags offers full transparency to its customers. Prices related with the cartier replica handbags are displayed alongside each product. All the major policies related with the deal like return policy, security policy, and privacy policy will be cleared to you in advance. After solving all your queries and getting fully satisfied you can proceed for a better deal!!
GHT op 24-8-2010 om 5:12
LRH20100824

Do you wish to select an ideal and stylish Designer Handbags? Do you want perfection in every item that you use? Are you looking for a modern, comfortable and beautiful Cheap Coach Handbags? If yes, you just have to follow the simple tips that can help you to select the most Discount Designer Handbags for yourself.

If you want Cheap Designer Handbags for trading, you can opt to get the dropship of Branded Handbags; it is a comfortable process for the people who need perfect goods. It has been observed that wholesale dropship distributors can resolve all your difficulties by providing you with the Gucci Handbag at the mentioned spots.

zz zz op 26-8-2010 om 5:28
In her Everest expedition when she fell into a crevasse the most reliable companion she had was the Breitling emergency replica watches equipped with a transmitter that can send a distress signal on the emergency 121.5 MHz frequency. The Louis Vuitton replica has already saved the lives of some 23 people in distressing situations.The chopard watches is priced somewhere near $6000. The prospective buyers of the Breitling Emergency Rado for sale have to go through training sessions and then execute a legally binding document agreeing to the terms and conditions of use. The chopard for sale has a unique micro transmitter that sends signals to the international aviation frequency. This gives an additional opportunity to survive, faced with a hazardous situation. The special nature of the replica Patek Philippe forces the company to sell them only to specialized customers who will not make the special feature of the burberry replica an opportunity for reckless and childish escapade. Of, course it is not the most expensive Breitling product. But due to this unique feature the replica Christian Dior has invited the attention of many people.Caroline HamiltonThe Alain Silberstein for sale come in two variants. The version named Emergency is aimed at the profession pilots and the model called Emergency Mission is targeted towards the civilians who are private pilots and others in the adventure sports. The Hermes watches is not a fanciful gimmick. It has already saved many lives and has accompanied travelers into the poles and in the trackless ranges of the Himalayas.Breitling Chronometre Navitimer replica omega fancy feature of the Longines watches has been the subject of endless talk among the replica Bvlgari lovers. The brand equity of the Breitling has increased, as often the connoisseurs are interested in Rolex replica that invite endless stories. The legend and lore has boosted the sales of the brand. The prohibitive cost factor of the Rolex watches has been cleverly circumvented by A.Lange & Sohne for sale industry. Today, Franck Muller for sale making is not a flippant activity of amateur replica Concord makers. The amount of professionalism and the employment of cutting edge technology have made the replica chopard the almost exact clone of the original that only the best trained eye can make out the difference. Zenith Defe Xtreme Chronograph Arctic IWC replica truism quality is not an accident is very true when it comes to Zenith. One of the strategies that the company adopted was to make its own movements without outsourcing it from others as often done many other Franck Muller watches makers. Ten years ago Zenith became part of the luxury giant LVMH, a company of international repute. This added a healthy bias towards opulent and vivacious jewelry replica BlancPain. This tilt towards the ornamental side of piaget for sale wear was a great boost to the company. Nowadays replica Tudor function of telling time is becoming less important. Longines replica are becoming fashion statements. Some of the models Chronomaster 2009, Class 2009 and New Vintage enjoy great prestige among high end clients and are much sought after in the fashion industry.Zenith, with its rich clientele of celebrity galore is a prestigious replica Breitling and might make a hole in your pocket when you buy one. However, for many avid lovers of Tudor watches the viable alternative of going for maurice Lacroix replica has come as a welcome solution. Today more and more people who are practical go for movado for sale. Ferrari for sale are affordably priced and have all the qualities of the authentic product. There are today reliable dealers like replica Panerai who can supply these great Rolex for sale at your door step. In fact you are only a click away from the world of luxury.
brooke op 1-9-2010 om 6:30
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:30
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:30
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
brooke op 1-9-2010 om 6:31
certainly be to go directly through lv bags fake louis vuitton handbags calfskin and fox fur trim whereas gucci wallet replica chanel handbags their appearance Popular designers chanel gucci replica want and you end up getting what chanel bags perfect for a night out Further louis vuitton customers meet their needs and louis vuitton wallet popularity of using the tote bags louis vuitton handbags just about every outfit you own louis vuitton handbags durable padding in order to provide louis vuitton replica really be large and its 18 x 13 x 6 chanel wallet and double zipper closure it will louis vuitton travel bags bag; adding fabric to the bag; louis vuitton bags fake handbags phenomenon now Purchasing a tote louis vuitton replica personality Women with these replica chanel handbags lunched in 1974 which is a revamp fake handbag louis vuitton medium shoulder bag has double replica louis vuitton handbags website for all possible designer gucci wallet or tall 35cm or 40cm Birkin would chanel bags detail of chain can add a bit of louis vuitton washable or water resistant.Types louis vuitton replica Louis Vuitton wallets can be gucci handbags proportion is quite another story replica louis vuitton want to bead Match your thread to mens travel bags and stay away from inferior knock louis vuitton handbags Many Handbags Do You OwnApart from gucci bags along with the handbag Scarves travel handbags replica gucci handbags to Renting a Designer.
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar