Delphi-PRAXiS
Seite 2 von 3     12 3      

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)

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.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:22 Uhr.
Seite 2 von 3     12 3      

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