Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi PayPal Soap Anbindung (https://www.delphipraxis.net/186422-paypal-soap-anbindung.html)

Harry29 2. Sep 2015 07:20

PayPal Soap Anbindung
 
Hallo zusammen,

ich kämpfe mit einer PayPal Soap Anbindung unter Delphi XE5.
PayPal erwartet einen individuellen Soap Header mit den Zugangsdaten.
In der importierten WSDL steht hier für die Klasse RequesterCredentials (TSoapHeader)bereit.
Hier nun ein einfaches Beispiel eines Aufrufes:

Delphi-Quellcode:
procedure TTest.PayPalTest;
var
  Soap: THTTPRIO;
  PayPal: PayPalAPIInterface;
  Sec: RequesterCredentials;
  Request: RefundTransactionReq;
  Response: RefundTransactionResponse;
begin
  Soap := THTTPRIO.Create(nil);
  try
    CoInitialize(nil);
    PayPal := GetPayPalAPIInterface(False, 'https://api-3t.sandbox.paypal.com/2.0/', Soap);

    Sec := RequesterCredentials.Create;
    Sec.Credentials := UserIdPasswordType.Create;
    Sec.Credentials.Username := 'hello world';
    Sec.Credentials.Password := 'hello world';
    Sec.Credentials.Signature := 'hello world';
    Soap.SOAPHeaders.Send(Sec);
    Soap.SOAPHeaders.SetOwnsSentHeaders(True);

    Request := RefundTransactionReq.Create;
    Request.RefundTransactionRequest := RefundTransactionRequest.Create;
    Response := PayPal.RefundTransaction(Request);
  finally
    FreeAndNil(Response);
    FreeAndNil(Request);
    CoUnInitialize;
  end;
end;
Wenn ich nun das Programm beende, meldet mein FastMM das es ein Speicherloch
im Bereich dieser RequesterCredentials gibt. Verstehe ich nicht, durch SetOwnsSentHeaders wird der Destructor
von RequesterCredentials nach dem Request aufgerufen.

Vielleicht hat hier jemand schon mal PayPal integriert und kann mir einen Tipp geben,
oder vielleicht mache ich generell etwas mit den Headern falsch, auch dann wäre ich um einen Tipp dankbar.

mkinzler 2. Sep 2015 07:24

AW: PayPal Soap Anbindung
 
Gib Sec einfach auch im finally frei.

Harry29 2. Sep 2015 07:45

AW: PayPal Soap Anbindung
 
Hhm. Nö, durch SetOwnsSentHeaders ist das ja eigentlich schon automatisch freigegeben.
Bein einem zusätzlichen Sec.Free im Finally gibts eine AV von FastMM.

Wenn ich SetOwnSentHeaders ausschalte und im Finally Sec.Free aufrufe,
habe ich im Übrigen weiterhin das Speicherloch.

TiGü 2. Sep 2015 08:30

AW: PayPal Soap Anbindung
 
Kenne mich mit der Thematik auch null aus, aber wenn man der Klasse RequesterCredentials einen Konstruktor und Destruktor verpasst, werden die auch erwartungsgemäß jeweils einmal durchlaufen.
Daran kann es also nicht liegen.
Wird vielleicht in den Unweiten des Frameworks sowas wie eine Kopie erzeugt?

Falls es jemanden weiterbringt, hier die Meldung vom externen FastMM (mit Delphi XE7):
Code:
--------------------------------2015/9/2 9:26:31--------------------------------
A memory block has been leaked. The size is: 52

This block was allocated by thread 0x474, and the stack trace (return addresses) at the time was:
406CC2 [System.pas][System][@GetMem$qqri][4391]
407AF3 [System.pas][System][TObject.NewInstance$qqrv][15641]
40821A [System.pas][System][@ClassCreate$qqrpvzc][16951]
685B2A [Soap.InvokeRegistry.pas][Soap.InvokeRegistry][Invokeregistry.TRemotable.$bctr$qqrv][2347]
6677E2 [Soap.OPToSOAPDomConv.pas][Soap.OPToSOAPDomConv][Optosoapdomconv.TSOAPDomConv.ReadHeader$qqrx47System.%DelphiInterface$20Xml.Xmlintf.IXMLNode%t1p31Soap.Invokeregistry.THeaderList][925]
66B4C5 [Soap.OPToSOAPDomConv.pas][Soap.OPToSOAPDomConv][Optosoapdomconv.TOPToSoapDomConvert.ProcessResponse$qqrx51System.%DelphiInterface$24Xml.Xmlintf.IXMLDocument%rx27Soap.Intfinfo.TIntfMetaDatarx28Soap.Intfinfo.TIntfMethEntryp31Soap.Invokeregistry.TInvContex
66B975 [Soap.OPToSOAPDomConv.pas][Soap.OPToSOAPDomConv][Optosoapdomconv.TOPToSoapDomConvert.ProcessResponse$qqrxp22System.Classes.TStreamrx27Soap.Intfinfo.TIntfMetaDatarx28Soap.Intfinfo.TIntfMethEntryp31Soap.Invokeregistry.TInvContextp31Soap.Invokeregistry.THea
675C6D [Soap.Rio.pas][Soap.Rio][Rio.TRIO.DoDispatch$qqrxp31Soap.Invokeregistry.TInvContextirx28Soap.Intfinfo.TIntfMethEntry][372]
67651B [Soap.Rio.pas][Soap.Rio][Rio.TRIO.Generic$qqrp23System.Rtti.TRttiMethodx42System.%DynamicArray$18System.Rtti.TValue%r18System.Rtti.TValue][530]
675FF2 [Soap.Rio.pas][Soap.Rio][Rio.TRIO.QueryInterface$15$ActRec.$0$Body$qqrp23System.Rtti.TRttiMethodx42System.%DynamicArray$18System.Rtti.TValue%r18System.Rtti.TValue][430]
469488 [System.Rtti.pas][System.Rtti][Rtti.TVirtualInterface.RawCallback$qqrpvx42System.%DynamicArray$18System.Rtti.TValue%r18System.Rtti.TValue][10027]

The block is currently used for an object of class: RequesterCredentials

The allocation number is: 6384

Current memory dump of 256 bytes starting at pointer address 7EC96C30:
DC AD 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 B9 1A 7D 80 80 80 80 00 00 00 00 F1 6D C9 7E
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F9 18 00 00 C2 6C 40 00 F3 7A 40 00 89 D8 40 00
1A 82 40 00 E2 50 60 00 3A 44 FE 76 A0 60 60 00 E8 61 60 00 A5 77 60 00 74 18 67 00 DD 2F 67 00
74 04 00 00 74 04 00 00 DE 6C 40 00 11 7B 40 00 65 82 40 00 69 51 60 00 11 D9 40 00 EB D7 40 00
C1 B4 40 00 B1 B3 40 00 8F 7C 40 00 0A 7B 40 00 65 82 40 00 2C 00 00 00 3C 22 60 00 D4 59 60 FC
90 72 73 00 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80
80 80 80 80 80 80 80 80 80 80 80 80 2B A6 9F 03 80 80 80 80 80 80 80 80 00 00 00 00 B1 6E C9 7E
Ü  *  i . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . ¹  . }  €  €  €  €  . . . . ñ  m É  ~
. . . . . . . . . . . . . . . . ù  . . .   l @  . ó  z @  . ‰  Ø  @  .
. ‚  @  . â  P `  . : D þ  v *  `  `  . è  a `  . ¥  w `  . t . g . Ý  /  g .
t . . . t . . . Þ  l @  . . {  @  . e ‚  @  . i Q `  . . Ù  @  . ë  ×  @  .
Á  ´  @  . ±  ³  @  .   |  @  . . {  @  . e ‚  @  . , . . . < " `  . Ô  Y `  ü
  r s . €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €  €
€  €  €  €  €  €  €  €  €  €  €  €  +  ¦  Ÿ  . €  €  €  €  €  €  €  €  . . . . ±  n É  ~

--------------------------------2015/9/2 9:26:31--------------------------------
This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

37 - 52 bytes: RequesterCredentials x 1

Dennis07 13. Jun 2018 04:14

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von Harry29 (Beitrag 1314361)
Delphi-Quellcode:
procedure TTest.PayPalTest;
var
  Soap: THTTPRIO;
  PayPal: PayPalAPIInterface;
  Sec: RequesterCredentials;
  Request: RefundTransactionReq;
  Response: RefundTransactionResponse;
begin
  Soap := THTTPRIO.Create(nil);
  try
    //...
    Request := RefundTransactionReq.Create;
    Request.RefundTransactionRequest := RefundTransactionRequest.Create;
    Response := PayPal.RefundTransaction(Request);
  finally
    FreeAndNil(Response);
    FreeAndNil(Request);
    CoUnInitialize;
  end;
end;

Ohne mich da jetzt reinlesen zu müssen, kann ich dir sagen, dass so eine Architektur, wie du sie geschrieben hast, nicht gerade sicher ist. Du kannst/solltest keine Objekte im "finally" freigeben, die du im "try" erst erzeugt hast. Das kann ganz leicht zu Zugriffsverletzungen führen. Und zwar beispielsweise immer dann, wenn im Konstruktor ein Fehler passiert, der nicht abgefangen wird. Der richtige Weg wäre 1. das Objekt erzeugen, dann in "try" damit arbeiten und später im "finally" freigeben. Dann bist du immer auf der sicheren Seite. Außerdem: Gewöhne dir an, alle Typen mit einem "T" und alle Interfaces mit einem "I" im Namen zu beginnen. Das macht es einfacher, sie zu identifizieren und von Standardtypen auseinander zu halten. Ich weiß jetzt nämlich zB nicht sicher, was bei dir ein Interface und was ein Klassen/Recordtypen hat. Das ließe sich leicht vermeiden. Ich sehe außerdem, dass du "Request.RefundTransactionRequest" erzeugst, aber nirgendwo freigibst. Ist es ein Interfaced-Typ mit Refcounting? Falls nein: Diesen MUSST du natürlich auch wieder freigeben, es reicht nicht, nur das Mutterobjekt freizugeben.

TiGü 13. Jun 2018 08:00

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von Dennis07 (Beitrag 1404684)
Zitat:

Zitat von Harry29 (Beitrag 1314361)
Delphi-Quellcode:
procedure TTest.PayPalTest;
var
  Soap: THTTPRIO;
  PayPal: PayPalAPIInterface;
  Sec: RequesterCredentials;
  Request: RefundTransactionReq;
  Response: RefundTransactionResponse;
begin
  Soap := THTTPRIO.Create(nil);
  try
    //...
    Request := RefundTransactionReq.Create;
    Request.RefundTransactionRequest := RefundTransactionRequest.Create;
    Response := PayPal.RefundTransaction(Request);
  finally
    FreeAndNil(Response);
    FreeAndNil(Request);
    CoUnInitialize;
  end;
end;

Außerdem: Gewöhne dir an, alle Typen mit einem "T" und alle Interfaces mit einem "I" im Namen zu beginnen. Das macht es einfacher, sie zu identifizieren und von Standardtypen auseinander zu halten. Ich weiß jetzt nämlich zB nicht sicher, was bei dir ein Interface und was ein Klassen/Recordtypen hat. Das ließe sich leicht vermeiden. Ich sehe außerdem, dass du "Request.RefundTransactionRequest" erzeugst, aber nirgendwo freigibst. Ist es ein Interfaced-Typ mit Refcounting? Falls nein: Diesen MUSST du natürlich auch wieder freigeben, es reicht nicht, nur das Mutterobjekt freizugeben.

Hast du schon mal mit Delphi und SOAP bzw. mit Quelltext gearbeitet, der per Import einer WSDL-Datei in die Delphi-IDE zu einer Delphi-Unit umgewandelt wurde?
Eher nicht, oder? :roll:

Die Typen fallen so aus dem Quelltextgenerator raus. Klar kann man die umbenennen oder Aliase mit T dazuschreiben, aber wozu der Aufwand?
Die Freigabe von RefundTransactionRequest geschieht mit ziemlicher Sicherheit im Destroy von Request.

Dennis07 13. Jun 2018 12:54

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1404697)
Hast du schon mal mit Delphi und SOAP bzw. mit Quelltext gearbeitet, der per Import einer WSDL-Datei in die Delphi-IDE zu einer Delphi-Unit umgewandelt wurde?
Eher nicht, oder? :roll:

Nö. :lol: Hattest du geschrieben dass es ein automatisch generierter Source ist? Falls ja, hab ich es überlesen :wall:

Zitat:

Zitat von TiGü (Beitrag 1404697)
Die Freigabe von RefundTransactionRequest geschieht mit ziemlicher Sicherheit im Destroy von Request.

"mit ziemlicher Sicherheit"? Das ist ja schön. :) Kannst du mal nachschauen, was im Konstruktor von "RefundTransactionRequest", beim Destruktor von "RefundTransactionReq", und in "CoUnInitialize" genau passiert?
Und selbst, wenn das so ist, wäre das eine weitere Fehlerquelle: Falls die Instanz (aus welchem Grund auch immer) der Parent class oder vom Childobjekt nicht erzeugt wurde, kriegst du 100% eine Fehlermeldung, im ersten Fall sogar noch ein Memory-Leak, da du ja das Objekt bereits erzeugt hast, was nun aber nicht zugewiesen werden kann.

TiGü 13. Jun 2018 14:27

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von Dennis07 (Beitrag 1404724)
Zitat:

Zitat von TiGü (Beitrag 1404697)
Hast du schon mal mit Delphi und SOAP bzw. mit Quelltext gearbeitet, der per Import einer WSDL-Datei in die Delphi-IDE zu einer Delphi-Unit umgewandelt wurde?
Eher nicht, oder? :roll:

Nö. :lol: Hattest du geschrieben dass es ein automatisch generierter Source ist? Falls ja, hab ich es überlesen :wall:

Zitat:

Zitat von TiGü (Beitrag 1404697)
Die Freigabe von RefundTransactionRequest geschieht mit ziemlicher Sicherheit im Destroy von Request.

"mit ziemlicher Sicherheit"? Das ist ja schön. :) Kannst du mal nachschauen, was im Konstruktor von "RefundTransactionRequest", beim Destruktor von "RefundTransactionReq", und in "CoUnInitialize" genau passiert?
Und selbst, wenn das so ist, wäre das eine weitere Fehlerquelle: Falls die Instanz (aus welchem Grund auch immer) der Parent class oder vom Childobjekt nicht erzeugt wurde, kriegst du 100% eine Fehlermeldung, im ersten Fall sogar noch ein Memory-Leak, da du ja das Objekt bereits erzeugt hast, was nun aber nicht zugewiesen werden kann.


Wenn du schon drei Jahre alte Threads ausgräbst - warum das Leute immer wieder machen entzieht sich meinem Verständnis - dann wäre es gut, wenn du
a) den ganzen Thread liest (man beachte hierbei die ersten drei Zeilen des Eröffnungsposts nach der Begrüßung von Harry29) und
b) Erfahrung mit den Dingen hast, über die gesprochen wird.

Es gilt die alte Regel: Wenn keine Erfahrung vorliegt -> nicht dazu posten!

Dennis07 13. Jun 2018 18:13

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1404738)
Wenn du schon drei Jahre alte Threads ausgräbst - warum das Leute immer wieder machen entzieht sich meinem Verständnis

https://www.facebook.com/groups/1370...8672088509186/ ...


Zitat:

Zitat von TiGü (Beitrag 1404738)
Es gilt die alte Regel: Wenn keine Erfahrung vorliegt -> nicht dazu posten!

Gut, dann tu das auch nicht! ...
Zitat:

Zitat von TiGü (Beitrag 1314374)
Kenne mich mit der Thematik auch null aus


Mal ganz abgesehen davon: Ich würde es begrüßen, wenn du mal einen angemessenen Ton anschlägst. Auf so ein Anmotzen hab ich hier offen gesagt keinen Bock.

makulik 13. Jun 2018 21:08

AW: PayPal Soap Anbindung
 
Hallo Zusammen,

Wenn, Ihr mit Eurem merkwürdigen Hin- und Her-Geplänkel hinsichtlich des angemessenen Tons in technischen Foren fertig seid, dann können wir uns ja mal wieder mit dem eigentlichen Problem beschäftigen (wer Lesen kann ist übrigens immer klar im Vorteil).

Derjenige, der das 3 Jahre alte Thema wieder ausgegraben hatte und nach einer Lösung sucht bin ich. Ich hatte eine entsprechende Anfrage in der FB Delphi Developer Group gestellt:

Memory leak with Delphi 10 Seattle generated Soap from PayPal WSDL.

Leider kam da bislang nicht mehr rüber, als wilde Spekulationen, wie die Speicherverwaltung unter Delphi funktioniert.
Embarcadero scheint das Problem ja offenbar nicht zu interessieren, sonst hätte man wohl einen Fix, oder wenigstens Vorschläge für einen Workaround mit einer fundierten Recherche finden können.
Ich habe das exakt gleiche Problem (mit einer wesentlich aktuelleren Delphi Version), und Source Code der Memory-Leaks produziert pflege ich nicht in Produktion zu geben; soviel dazu und genug zum Vorwort.

--------------------------------------------------------

Also zunächst mal liegt das Problem nicht an dem RequesterCredentials Objekt, das für den SOAP Request erzeugt wurde (der Soap.SOAPHeaders.SetOwnsSentHeaders(True); Mechanismus funktioniert wie er soll), sondern das für den SOAP Response vom PayPal Server ebenfalls ein RequesterCredentials SOAP Header (mit leeren Feldern) zurückgesandt wird und vom Parser ein entsprechendes Objekt instanziiert wird.

Das kann man eigentlich auch schön in TiGü's FastMM Stack Trace sehen:

Delphi-Quellcode:
[Optosoapdomconv.TOPToSoapDomConvert.ProcessResponse$qqrxp22System.Classes.TStreamrx27Soap.Intfinfo.TIntfMetaDatarx28Soap.Intfinfo.TIntfMethEntryp31Soap.Invokeregistry.TInvContextp31Soap.Invokeregistry.THea
ProcessResponse ist diesbezüglich ziemlich unmissverständlich.

Die SOAP Header werden nun leider nicht automatisch mit dem decodierten SOAP Body Object freigegeben, soweit ich den Source Code analysiert habe.

Die Response Header werden in einer generischen TObjectList<T> von TSOAPHeaders.FInHeaders (oder so) gespeichert, für die die OwnsObjects Eigenschaft auf true gesetzt ist, soweit ich das heute debugged habe.
D.h. im Klartext, dass die Response Header Objekte eigentlich spätestens beim Zerstören des TSOAPHeaders Objekts abgeräumt werden sollten(!) was aber aus irgendeinem obskuren Grund nicht passiert (TObjectList<T> funktioniert eigentlich ausnahmsweise einwandfrei, soweit mir bekannt ist). Daher das finale Memory-Leak.

Was mir beim Debuggen heute auch noch aufgefallen ist, ist dass das in der Liste gespeicherte TSOAPHeader Objekt von der RTTI nicht als RequesterCredentials Typ erkannt wird, sondern als dessen (generierte) Basis Klasse CustomSecurityHeaderType.

Der vom WSDL Component Generator erzeugte Code sieht so aus:

Delphi-Quellcode:
   RequesterCredentials = class(CustomSecurityHeaderType)
   private
   published
   end;
Ich habe versucht mittels

Delphi-Quellcode:
procedure MyProcedure();
var
    headerAccess : ISOAPHeaders;
    respCredentials : RequesterCredentials;
begin
    // ...

    respCredentials := headerAccess.Get(RequesterCredentials) as RequesterCredentials;

    // ...
    respCredentials.Free();
end;
das Header Objekt explizit zu holen und freizugeben, was allerdings scheitert, da das in der Liste gespeicherte Objekt tatsächlich vom Typ CustomSecurityHeaderType ist.

Weiter bin ich heute nicht mehr gekommen, aber ich glaube ich bin der Lösung (dem Workaround) schon ziemlich nahe.

Der ganze generierte Kram aus dem WSDL basiert übrigens als Super-Super Klasse auf TObject, und leider nicht auf TInterfacedObject und entsprechenden Reference Counted Interface Objekten (TRemotable ist leider nur ein TObject ohne Interface), was die ganze Angelegenheit (Speicherverwaltung) wohl erheblich vereinfachen würde.

Sobald ich in der Lage bin Weiteres zur Lösung des Problems beizutragen, werde ich dies hier tun (morgen hoffentlich).

Gruss,
Günther

P.S.:
Wenn irgendjemand weitere konstruktive und fundierte Beiträge zur Lösung des Problems beitragen möchte, ist dies ausdrücklich erwünscht!

makulik 14. Jun 2018 11:12

AW: PayPal Soap Anbindung
 
Leider ist es so, dass tatsächlich Embarcadero den Fehler (das Memory-Leak) verursacht.

Das Problem ist der folgende Code in der Funktion TSOAPDomConv.ConvertSOAPToObject aus Soap.OPToSOAPDomConv.pas ab Zeile 4768:

Delphi-Quellcode:
function TSOAPDomConv.ConvertSOAPToObject(RootNode, Node: IXMLNode; AClass: TClass;
  const URI, TypeName: InvString; ObjP: Pointer): TObject;

// ...
    if Assigned(Obj) and LegalRef then
    begin
     if (NodeClass <> nil) and (NodeClass <> Obj.ClassType) then
        Obj.Free(); // Dieses Free hat gefehlt, die alte Instanz von Obj dangled nach dem Überschreiben
        Obj := NodeClass.Create;
    end

// ...
Ist auch in der aktuellen Delphi Version (Tokyo) noch vorhanden. Vermutlich gibt es auch noch weitere Stellen die ein ähnliches Problem haben.
TObject ist nun mal nicht ref-counted :(
Ich werde ein Ticket bei Embarcadero aufmachen.

Gruss,
Günther

mkinzler 14. Jun 2018 12:24

AW: PayPal Soap Anbindung
 
Dann sollte man die beiden Zeilen aber auch mit begin..end umschliessen.

TiGü 14. Jun 2018 12:53

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1404813)
Leider ist es so, dass tatsächlich Embarcadero den Fehler (das Memory-Leak) verursacht.

Das Problem ist der folgende Code in der Funktion TSOAPDomConv.ConvertSOAPToObject aus Soap.OPToSOAPDomConv.pas ab Zeile 4768:

Delphi-Quellcode:
function TSOAPDomConv.ConvertSOAPToObject(RootNode, Node: IXMLNode; AClass: TClass;
  const URI, TypeName: InvString; ObjP: Pointer): TObject;

// ...
    if Assigned(Obj) and LegalRef then
    begin
     if (NodeClass <> nil) and (NodeClass <> Obj.ClassType) then
        Obj.Free(); // Dieses Free hat gefehlt, die alte Instanz von Obj dangled nach dem Überschreiben
        Obj := NodeClass.Create;
    end

// ...
Ist auch in der aktuellen Delphi Version (Tokyo) noch vorhanden. Vermutlich gibt es auch noch weitere Stellen die ein ähnliches Problem haben.
TObject ist nun mal nicht ref-counted :(
Ich werde ein Ticket bei Embarcadero aufmachen.

Gruss,
Günther



Wenn man sich in der Methode den XML-Text vom RootNode anschaut sieht man folgendes:

Code:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cc="urn:ebay:apis:CoreComponentTypes" xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext" xmlns:ed="urn:ebay:apis:EnhancedDataTypes" xmlns:ebl="urn:ebay:apis:eBLBaseComponents" xmlns:ns="urn:ebay:api:PayPalAPI">
   <SOAP-ENV:Header>
      <Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext" xsi:type="wsse:SecurityType"/>
      <RequesterCredentials xmlns="urn:ebay:api:PayPalAPI" xsi:type="ebl:CustomSecurityHeaderType">   <<<--------- Hier ist der Übertäter
         <Credentials xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:UserIdPasswordType"/>
      </RequesterCredentials>
   </SOAP-ENV:Header>
   <SOAP-ENV:Body id="_0">
      <RefundTransactionResponse xmlns="urn:ebay:api:PayPalAPI">
         <Timestamp xmlns="urn:ebay:apis:eBLBaseComponents">2018-06-14T11:41:34Z</Timestamp>
         <Ack xmlns="urn:ebay:apis:eBLBaseComponents">Failure</Ack>
         <CorrelationID xmlns="urn:ebay:apis:eBLBaseComponents">ac2341fd2df09</CorrelationID>
         <Errors xmlns="urn:ebay:apis:eBLBaseComponents" xsi:type="ebl:ErrorType">
            <ShortMessage xsi:type="xs:string">Authentication/Authorization Failed</ShortMessage>
            <LongMessage xsi:type="xs:string">You do not have permissions to make this API call</LongMessage>
            <ErrorCode xsi:type="xs:token">10002</ErrorCode>
            <SeverityCode xmlns="urn:ebay:apis:eBLBaseComponents">Error</SeverityCode>
         </Errors>
         <Version xmlns="urn:ebay:apis:eBLBaseComponents">0.000000</Version>
         <Build xmlns="urn:ebay:apis:eBLBaseComponents">000000</Build>
         <RefundTransactionID/>
      </RefundTransactionResponse>
   </SOAP-ENV:Body></SOAP-ENV:Envelope>
Aufgrund von
Code:
xsi:type="ebl:CustomSecurityHeaderType"
wird ja in den Zeilen
Delphi-Quellcode:
  GetElementType(ObjNode, NodeURI, NodeTypeName);
  NodeClass := RemTypeRegistry.URIToClass(NodeURI, NodeTypeName, IsScalar);
die NodeClass auf CustomSecurityHeaderType gesetzt. Mit den bekannten Folgen.

Also Workaround - welche negativen Auswirkungen das hat, muss gesondert untersucht werden - kannst du in der PayPalSvc-Unit ca. bei Zeile 28049 das folgende auskommentieren:
Delphi-Quellcode:
...
RemClassRegistry.RegisterXSClass(CustomSecurityHeaderType, 'urn:ebay:apis:eBLBaseComponents', 'CustomSecurityHeaderType');
...

Dadurch wird RemTypeRegistry.URIToClass nichts finden und NodeClass wird zu nil.
Damit bleibt das Obj (=> RequesterCredentials) erhalten und das Speicherleck ist weg.

makulik 14. Jun 2018 18:02

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von mkinzler (Beitrag 1404819)
Dann sollte man die beiden Zeilen aber auch mit begin..end umschliessen.

Oops, ja natürlich, Danke.
Es hat glücklicherweise trotzdem funktioniert (ein sehr spezieller Fall.)

makulik 14. Jun 2018 18:11

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1404823)
Also Workaround - welche negativen Auswirkungen das hat, muss gesondert untersucht werden - kannst du in der PayPalSvc-Unit ca. bei Zeile 28049 das folgende auskommentieren:
Delphi-Quellcode:
...
RemClassRegistry.RegisterXSClass(CustomSecurityHeaderType, 'urn:ebay:apis:eBLBaseComponents', 'CustomSecurityHeaderType');
...

Dadurch wird RemTypeRegistry.URIToClass nichts finden und NodeClass wird zu nil.
Damit bleibt das Obj (=> RequesterCredentials) erhalten und das Speicherleck ist weg.

Hmm, ich weiss nicht recht. Für die ausgehenden SOAP Requests muss das ja trotzdem funktionieren, und ich weiss nicht ob es so eine gute Idee ist die Klassenregistrierung zu entfernen.

Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben.

TiGü 15. Jun 2018 08:26

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1404851)
Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben.

Wenn das geht, ist dies ein sinnvoller Weg.

Das Problem, fehlerhafte oder schlecht programmierten Code aus dem Framework ersetzen zu müssen, hatte ich auch schon öfters.
Am Einfachsten ist es noch, wenn man von der betreffenden Klasse ableiten kann und die zu überschreibende Methode protected/public und virtual/dynamic ist.

makulik 15. Jun 2018 10:06

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1404896)
Zitat:

Zitat von makulik (Beitrag 1404851)
Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben.

Wenn das geht, ist dies ein sinnvoller Weg.

Das Problem, fehlerhafte oder schlecht programmierten Code aus dem Framework ersetzen zu müssen, hatte ich auch schon öfters.
Am Einfachsten ist es noch, wenn man von der betreffenden Klasse ableiten kann und die zu überschreibende Methode protected/public und virtual/dynamic ist.

Ist leider keine virtuelle Methode, aber die Schnittstelle erlaubt es einen eigenen Converter zu konfigurieren.

TiGü 15. Jun 2018 10:50

AW: PayPal Soap Anbindung
 
So hatte ich das auch verstanden.

makulik 16. Jun 2018 13:26

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1404851)
Zitat:

Zitat von TiGü (Beitrag 1404823)
Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben.


:wall:

Leider erweist sich diese Hoffnung als Trugschluss :(

Die Superklasse TRIO von THTTPRIO hat zwar die definierte Eigenschaft Converter vom (Interface) Typ IOPConverter, THTTPRIO will aber dennoch den Typ Soap.TOPToSoapDomConvert.

Was ist denn das für ein #&$**!!@ Design bitteschön???

Also kein "sauberer" Workaround möglich, es bleibt bei dem Patch (den ich jezt auch noch 2x pflegen muss, für Seattle und Tokyo, da andere Änderungen an Soap.OPToSoapDomConvert.pas vorgenommen wurden).
So ein Saftladen!! :|

P.S.:
Für jeden den's interessiert, hier ist das Ticket bei Embarcadero: RAD StudioRSP-20730

TiGü 18. Jun 2018 14:24

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1404961)
Zitat:

Zitat von makulik (Beitrag 1404851)
Zitat:

Zitat von TiGü (Beitrag 1404823)
Für einen "sauberen" Workaround (ohne die gefixte Unit in das .dpr direkt einzubinden) dachte ich eher daran den Original Code zu klonen, und den gefixten Converter explizit an das entsprechende Interface zu übergeben.


:wall:

Leider erweist sich diese Hoffnung als Trugschluss :(

Die Superklasse TRIO von THTTPRIO hat zwar die definierte Eigenschaft Converter vom (Interface) Typ IOPConverter, THTTPRIO will aber dennoch den Typ Soap.TOPToSoapDomConvert.

Was ist denn das für ein #&$**!!@ Design bitteschön???

Also kein "sauberer" Workaround möglich, es bleibt bei dem Patch (den ich jezt auch noch 2x pflegen muss, für Seattle und Tokyo, da andere Änderungen an Soap.OPToSoapDomConvert.pas vorgenommen wurden).
So ein Saftladen!! :|

P.S.:
Für jeden den's interessiert, hier ist das Ticket bei Embarcadero: RAD StudioRSP-20730

Du kannst es - zumindest für Tokyo - wie folgt lösen.
Der entscheidende Punkt ist das Freigeben der betreffenden Klasse in der überschriebenen ConvertSoapToNativeData-Methode.

Delphi-Quellcode:
unit Paypal.View;

interface

uses
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Controls, Vcl.Forms, Vcl.StdCtrls,
  Winapi.ActiveX,
  PayPalSvc,
  Soap.SOAPHTTPClient,
  Soap.InvokeRegistry,
  System.TypInfo,
  Soap.Rio,
  Soap.OPToSOAPDomConv,
  Xml.XMLIntf,
  Soap.SOAPConst,
  Soap.TypeTrans;

type
  TfrmPayPal = class(TForm)
    Button1: TButton;

    procedure Button1Click(Sender: TObject);
  private
  public
  end;

var
  frmPayPal: TfrmPayPal;

implementation

{$R *.dfm}


type
  TMyConverter = class(Soap.OPToSOAPDomConv.TOPToSoapDomConvert)
  public
    procedure ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo;
      RootNode, Node: IXMLNode; Translate: Boolean); override;
  end;

type
  PTObject = ^TObject;

procedure TMyConverter.ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo;
  RootNode, Node: IXMLNode; Translate: Boolean);
var
  TypeUri, TypeName: InvString;
  IsNull: Boolean;
  Obj, DataObj: TObject;
  P: Pointer;
  ID: InvString;
begin
  Node := GetDataNode(RootNode, Node, ID);
  IsNull := NodeIsNull(Node);
  if TypeInfo.Kind = tkVariant then
  begin
    if IsNull then
    begin
      Variant(PVarData(DataP)^) := NULL;
    end
    else
      ConvertSoapToVariant(Node, DataP);
  end
  else if TypeInfo.Kind = tkDynArray then
  begin
    P := DataP;
    P := ConvertSoapToNativeArray(P, TypeInfo, RootNode, Node);
    Pointer(DataP^) := P
  end
  else if TypeInfo.Kind = tkClass then
  begin
    Obj := ConvertSOAPToObject(RootNode, Node, GetTypeData(TypeInfo).ClassType, TypeUri, TypeName, DataP);
    DataObj := PTObject(DataP)^;
    if Assigned(Obj) and Assigned(DataObj) and (Obj.ClassType <> DataObj.ClassType) then
    begin
      DataObj.Free; // <--- hier geben wir das Objekt frei, wenn die Class Types verschieden sind!
    end;

    PTObject(DataP)^ := Obj
  end
  else
  begin
    if Translate then
    begin
      if not TypeTranslator.CastSoapToNative(TypeInfo, GetNodeAsText(Node), DataP, IsNull) then
        raise ESOAPDomConvertError.CreateFmt(STypeMismatchInParam, [Node.nodeName]);
    end;
  end;
end;

procedure TfrmPayPal.Button1Click(Sender: TObject);
var
  Rio: THTTPRIO;
  PaypalInterface: PayPalAPIInterface;
  Sec: RequesterCredentials;
  Request: RefundTransactionReq;
  Response: RefundTransactionResponse;
begin
  CoInitializeEx(nil, 0);
  try
    Rio := THTTPRIO.Create(nil);
    Rio.Converter := TMyConverter.Create(Rio);
    PaypalInterface := GetPayPalAPIInterface(False, 'https://api-3t.sandbox.paypal.com/2.0/', Rio);

    Sec := RequesterCredentials.Create;
    Sec.Credentials := UserIdPasswordType.Create;
    Sec.Credentials.Username := 'hello world';
    Sec.Credentials.Password := 'hello world';
    Sec.Credentials.Signature := 'hello world';

    Rio.SOAPHeaders.Send(Sec);
    Rio.SOAPHeaders.SetOwnsSentHeaders(True);

    Request := RefundTransactionReq.Create;
    try
      Request.RefundTransactionRequest := RefundTransactionRequest.Create;

      Response := PaypalInterface.RefundTransaction(Request);
      if Assigned(Response) then
      begin
        try
          {
            Hier machen wir was Kluges mit dem Response-Objekt
          }
        finally
          Response.Free;
        end;
      end;
    finally
      Request.Free;
    end;
  finally
    PaypalInterface := nil; // nur um zu verdeutlichen, dass hier Schluss ist!
    CoUnInitialize;
  end;
end;

end.

makulik 20. Jun 2018 02:55

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1405096)
Du kannst es - zumindest für Tokyo - wie folgt lösen.
Der entscheidende Punkt ist das Freigeben der betreffenden Klasse in der überschriebenen ConvertSoapToNativeData-Methode.

Delphi-Quellcode:
procedure ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo;
      RootNode, Node: IXMLNode; Translate: Boolean); override;
                                                  // ^^^^^^^^

D.h. in Tokyo sind diese Funktionen von TOPToSoapDomConvert jetzt virtuell und überladbar?

Ich hatte nicht so genau darauf geachtet, und wollte nur unseren CI Server so schnell wie möglich wieder auf green state bringen (für unseren Tokyo Branch).
Also habe ich nur den Patch für die aktuelle Version der Unit in Tokyo angepasst (5 min).

Nichtsdestotrotz finde ich das Mem-Leak im Original Code schlicht inakzeptabel, und in der Unit sind weitere fehlerhafte Stellen dieser Art zu finden.
Ich kann nur annehmen, dass das nur mit der ARC Compiler Version getestet wurde (auto ref-counting für TObject Sub-Klassen), in der wohlwollenden Annahme, das die Embarcadero Devs wirklich Unit Tests für das "Zeuch" geschrieben haben.

Ich werde Deinen Vorschlag für einen Workaround ausprobieren (vielen Dank für den Beispielcode!!). Wenn Embarcadero keinen Fix bis Okt. 2018 (unser Termin für die Umstellung des Production Codes von Seattle auf Tokyo) liefert, ist das wahrscheinlich die bessere Lösung, als die originale RTL Unit zu patchen.

Es ist schon traurig, dass man überhaupt einen Workaround implementieren muss, für eine Toolchain + Framework in dieser Preisklasse :-(.

jaenicke 20. Jun 2018 06:34

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1405254)
Wenn Embarcadero keinen Fix bis Okt. 2018 (unser Termin für die Umstellung des Production Codes von Seattle auf Tokyo) liefert

Ist denn auch ein direkter Supportfall bei Embarcadero offen oder nur dieses öffentliche Ticket? Eine möglichst schnelle Lösung bekommst du eher, wenn du dafür einen Supportfall aufmachst. (Drei pro Jahr sind ja in der Subscription enthalten.)

TiGü 20. Jun 2018 07:39

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von makulik (Beitrag 1405254)
Zitat:

Zitat von TiGü (Beitrag 1405096)
Du kannst es - zumindest für Tokyo - wie folgt lösen.
Der entscheidende Punkt ist das Freigeben der betreffenden Klasse in der überschriebenen ConvertSoapToNativeData-Methode.

Delphi-Quellcode:
procedure ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo;
      RootNode, Node: IXMLNode; Translate: Boolean); override;
                                                  // ^^^^^^^^

D.h. in Tokyo sind diese Funktionen von TOPToSoapDomConvert jetzt virtuell und überladbar?

Sowohl in Berlin als auch für Tokyo ist die public-Methode
Delphi-Quellcode:
ConvertSoapToNativeData
als
Delphi-Quellcode:
dynmamic
gekennzeichnet und daher überschreibbar.
Der Fix würde also für beide Versionen funktionieren.

Zitat:

Zitat von makulik (Beitrag 1405254)
Ich werde Deinen Vorschlag für einen Workaround ausprobieren (vielen Dank für den Beispielcode!!).

Gern geschehen! :)

Zitat:

Zitat von makulik (Beitrag 1405254)
Es ist schon traurig, dass man überhaupt einen Workaround implementieren muss, für eine Toolchain + Framework in dieser Preisklasse :-(.

Beschäftige dich dann mal lieber nicht mit dem internen REST-Framework. :spin2:

Ich musste in Delphi Berlin darüber eine Datei schicken, was ums verrecken nicht funktionieren wollte.
Ein hinterherdebuggen zeigte, dass die gesamte Funktionalität dafür noch nicht da war bzw. auskommentiert.
Erst der Umstieg auf Tokyo und wieder eine Überschreibung einer wichtigen Methode brachte die Lösung.

Leider hat Emba die Unart, viele wichtige Klassendefinitionen nur im implementation-Teil zu schieben, was einige Fixes sehr erschwert.

makulik 20. Jun 2018 17:39

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von jaenicke (Beitrag 1405256)
Ist denn auch ein direkter Supportfall bei Embarcadero offen oder nur dieses öffentliche Ticket? Eine möglichst schnelle Lösung bekommst du eher, wenn du dafür einen Supportfall aufmachst. (Drei pro Jahr sind ja in der Subscription enthalten.)

Danke für den Tip, aber die Supportfälle heben wir uns vielleicht dann doch für wichtigere (unlösbare) Probleme auf, denke ich.

Ich hab' einfach einem ihrer Chefingenieure und Evangelisten (Jim Mc Keeth) auf Twitter ein bissl in den Arsch getreten:

https://twitter.com/GntherMakulik/st...98391128297473

:lol:

makulik 20. Jun 2018 17:42

AW: PayPal Soap Anbindung
 
Zitat:

Zitat von TiGü (Beitrag 1405259)
Leider hat Emba die Unart, viele wichtige Klassendefinitionen nur im implementation-Teil zu schieben, was einige Fixes sehr erschwert.

Ich pflege diese "Unart" auch, aber dann habe ich eine richtige interface Definition für die Implementierung.


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:01 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz