Posts Tagged: ‘COM Classes’

Konsum mal anders

5. August 2011 Posted by Nils Diesendorf

Vor einigen Wochen sah ich mich mit der Fragestellung konfrontiert, wie man bestimmte Daten aus einer Notesdatenbank am besten in ein SAP System uebermitteln kann.
Schnell war klar, dass eine Kopplung der beiden Systeme ueber eine CSV-Datei Schnittstelle eher "oldschool" ist und nicht in Frage kommt. Wer sich schon einmal eingehend mit 2 Systemen die Daten ueber eine CSV Schnittstelle austauschen beschaeftigt hat, weiss wovon ich rede ;)
 
Und so geisterte schnell das Wort "Web Service" durch die vom Rauch vieler Koepfe vernebelte Luft und es war beschlossene Sache: Ein Web Service Consumer musste her.

Nachdem die bereitgestellte WSDL Datei nach einigen Schwierigkeiten wie zu langen Funktionsnamen  ("InternalError_Bic_cfdominoWs1RfcExceptions_n0") und der Unmoeglichkeit das WSDL im DominoDesigner als Java Webservice einzubinden, endlich als WebService Consumer bereitstand, konnte die eigentliche Arbeit beginnen.

Es wurde ein Agent erstellt, der WebService ueber ein "use" eingebunden und schon lies sich in einem LS Agenten ein entsprechendes Objekt dimensionieren und ueber die Methode "setcredentials" sogar mit Login Parametern fuer das SAP System ausstatten. Als naechstes wurden unzaehlige Dokumente geladen, Werte ausgelesen und hoechst umstaendlich in den entsprechenden WS Klassen bereitgestellt.  Folgendes Beispiel soll aufzeigen, wie "holprig" so eine WS klassen Definition in LS daherkommt. Betrachten wir dazu die folgenden drei Basisklassen

Klasse zur Repraesentation eines einzelnen Datentyps
Class objDATENTYP1 as XSD_ANYTYPE
      Public value As String
       
        Sub NEW
        End Sub
end Class

Klasse zur Repraesentation eines einzelnen Objektes / Datensatzes:
Class objEinzelnesObjekt as XSD_ANYTYPE
      Public objVariable1 As objDATENTYP1

      Sub New
     End Sub
End Class

Klasse zur Repraesentation einer Liste der Objekte / Datensaetze:
Class Listenobjekt_n0 As XSD_ANYTYPE
        Public item() As objEinzelnesObjekt
       
        Sub NEW
        End Sub
End Class

Die Anzahl an Klassen fuehrt im Programmablauf dann zu folgender Dimensionierungs und Zuweisungsorgie um einen einzelnen Wert an den Web Service zu uebergeben:

'//-- Dimensionierung der Objekte -----------
Dim objItemList As New Listenobjekt  
Redim objItemList.item(0) As objEinzelnesObjekt '//-- bei mehreren Objekten muss hier natuerlich statt der 0 eine groessere Zahl stehen ;)

Dim objSingleItem As New objEinzelnesObjekt
Dim objVariable As New objDATENTYP1

'// --- Zuweisung der Werte zum Daten Objekt -------
objVariable.Value = "verify nice value"

'//-- Zuweisung des Datenobjektes zum einzelnen Objekt
Set objSingleItem.objVariable1 = objVariable

'//-- Zuweisung des einzelnen Objektes zum Listen Objekt
Set objItemList.item(0) = objSingleItem

Nun bleibt nur noch, dass ganze an den WS zu uebergeben, was wie folgt geschieht:

Dim objWSConnect As New WSServiceObject_aus_WSDL / (PortTypeBAse)
Call objWSConnect.Invoke_Function_in_WSDL(objItemList, Fault1)

Hat man bis dahin alles richtig gemacht, so passiert - nichts. Eine Rueckmeldung ob die Uebergabe der Daten erfolgreich war, waere schoen gewesen, blieb aber aus. Dafuer erreichten wir immerhin, dass bei einer falschen Datenstruktur (z.B. String laenger als im WSDL definiert) sich der Errorhandler regte.

Doch auch hier lies sich lediglich eine "Es ist ein Fehler aufgetreten. Kontaktieren sie ihren Datenbank Administrator" Meldung erzeugen. Bei einer entsprechenden Anzahl von Objekten wird es so unmoeglich, die Fehlerquelle zu lokalisieren und abzustellen - die Routine ist also fuer einen Regelbetrieb denkbar ungeeignet.

Bei der aufrufenden Funktion faellt auf, dass ein "Fault1" Objekt vom Typ "WS_Fault" erwartet bzw. zurueckgeliefert wird. Dieses Objekt wird ueber das WSDL File erzeugt und stellt sich wie folgt dar.:
Class Fault1 As WS_FAULT
       
        Public Name As Bic_cfdWs1RfcExceptions_n0
        Public Text As XSD_STRING
        Public Message As RfcExceptionMessage_n0
       
        Sub NEW
        End Sub
       
End Class

Doch auch nach der Initialisierung der entsprechenden Objekte und Klassen, konnte der Grund fuer den Fehler nicht ermittelt werden. Das FehlerObjekt blieb hartnaeckig ungefuellt, obwohl bei dem zum Testen der Verbindungen verwendeten Programm SoapUI der Fehlergrund und Fehlerstelle eindeutig angezeigt wurde.

Wie nun also dem Fehler auf die Schliche kommen ?

Wir entschieden uns schweren Herzens, den Notes Weg zu verlassen und das auch in der von Niklas Heidloff und Simon O'Doherty erstellten Soap Catcher Datenbank  verwendete COM Objekt Microsoft.XMLHTTP einzusetzen.

     Dim WSObject As Variant
        Set WSObject = CreateObject("Microsoft.XMLHTTP")
       
        Dim strREQUEST As String

        strREQUEST = {<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:sap-com:document:sap:soap:functions:mc-style">}
        strREQUEST = strREQUEST & "<soapenv:Header/><soapenv:Body><urn:webservice><Data>"
        strREQUEST = strREQUEST & "<item>"
        strREQUEST = strREQUEST & "<Variable1>" & "verify nice value" & "</Variable1>"
        strREQUEST = strREQUEST & "</item>"
               
        strREQUEST = strREQUEST & "</Data>"
        strREQUEST = strREQUEST & "</urn:webservice>"
        strREQUEST = strREQUEST & "</soapenv:Body>"
        strREQUEST = strREQUEST & "</soapenv:Envelope>"
       

        WSObject.open "POST", endPoint, False, strUsername, strPassword
        WSObject.setRequestHeader "Content-type", "text/xml;charset=UTF-8"
        WSObject.send(strREQUEST)
       
     
        Dim strError As String
        strError = StrRight(StrLeft(StrRight(httpObject.responseText,"</faultcode>"),"</faultstring>"),">")
       
Nachdem man mittels .send die Daten abgeschickt hat, kann ueber die Eigenschaft  httpObject.responseText nun die entsprechende Rueckmeldung (Fehler o. OK) ausgelesen werden.

FAZIT  
Bei der hier aufgezeigten Methode die Soap Meldungen per Microsoft COM Objekt zu laden, bestehen die ueblichen Einschraenkungen. Allen voran sei erwaehnt, dass die Verwendung auf Microsoft fremden System eher schwierig ist. Auch unterliegt der hier verwendete String "strRequest" natuerlich den entsprechenden Groessenbeschraenkungen.
Die Verwendung eines WebServices mit Domino 8.5.2 Bordmitteln koennte man generell eher als "schwierig" bezeichnen. Es waere wuenschenswert, dass in den entsprechenden WS_Fault Objekten o.ae. zumindest die SOAP Antworten zurueckgeliefert werden wuerden, um diese auszuwerten. Aber vielleicht wird es ja was mit 8.5.5 .