Letztens habe ich für einen Kunden an einer Demo Anwendung für die Verwendung von Web Service Consumer in LotusScript gearbeitet. Dabei habe ich ziemlich lange die Ursache für einen java.lang.NullPointerException
Fehler gesucht. Dieser Blogeintrag richtet sich an jeden, der vielleicht in das gleiche Problem hineinläuft. Denn ehrlich gesagt, war die Lösung im Nachhinein betrachtet, sehr einfach.
Ich verrate ihn erst die Lösung und die technischen Einzelheiten ein bisschen weiter unten. Auch wenn Sie die technischen Einzelheiten im Moment nicht interessieren, sollten Sie sich einen Punkt merken:
LotusScript Web Services Consumer und Provider verwenden intern Java und Java unterscheidet zwischen Groß- und Kleinschreibung. Wenn man dieses nicht berücksichtigt, verliert man Stunden bei der Suche nach einem Fehler, der nicht existiert.
Hier sind die technischen Details.
Bei der Demo-Anwendung ging es unter anderem um die verschiedenen Typen von WSDL Dateien bei der Implementierung von Web Service Providern. Es existiert ein großartiger Artikel auf IBM developerWorks Which style of WSDL should I use? zu dem Thema. Er ist sehr zu empfehlen!
Um die Unterschiede zwischen RPC/encoded und Document/literal WSDL Typen zu zeigen, habe ich zwei Web Service Provider implementiert. Beide Provider verwenden den absolut identischen LotusScript Code.
Dann implementierte ich zwei Web Service Consumer, in dem ich die WSDL Dateien von den zuvor erstellten Web Service Providern importierte. Hier sind nun die beiden entscheidenden Methoden aus den Web Service Consumern.
Web Service Consumer Aufruf eines RPC/encoded Web Service Provider
Function checkout_status(auftragnr As String) As String If Not IsDebugMode Then On Error Goto errorHandler On Error lsERR_NOTES_WSENGINE_UNINIT_METHOD_ARG Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_NOTINIT Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_ERROR Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_METHOD_ERROR Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_METHOD_FAULT Goto wsErrorHandler Let checkout_status = Service.Invoke("checkout_status", auftragnr) Exit Function errorHandler: If HandleErrorWithContext(CreateErrorContext(Nothing,|auftragnr: "| & auftragnr & |"| )) = RESUME_NEXT_LINE Then Resume Next Call RethrowError() Exit Function wsErrorHandler: If HandleErrorWithContext(CreateErrorContext(Nothing,|auftragnr: "| & auftragnr & |"| )) = RESUME_NEXT_LINE Then Resume Next Call CreateWebServiceLogEntry(Me, |auftragnr: "| & auftragnr & |"|, Nothing) Call RethrowError() End Function
Web Service Consumer Aufruf eines Document/literal Web Service Provider
Function checkout_status(auftragnr As String) As String If Not IsDebugMode Then On Error Goto errorHandler On Error lsERR_NOTES_WSENGINE_UNINIT_METHOD_ARG Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_NOTINIT Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_ERROR Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_METHOD_ERROR Goto wsErrorHandler On Error lsERR_NOTES_WSENGINE_METHOD_FAULT Goto wsErrorHandler Let checkout_status = Service.Invoke("CHECKOUT_STATUS", auftragnr) Exit Function errorHandler: If HandleErrorWithContext(CreateErrorContext(Nothing,|auftragnr: "| & auftragnr & |"| )) = RESUME_NEXT_LINE Then Resume Next Call RethrowError() Exit Function wsErrorHandler: If HandleErrorWithContext(CreateErrorContext(Nothing,|auftragnr: "| & auftragnr & |"| )) = RESUME_NEXT_LINE Then Resume Next Call CreateWebServiceLogEntry(Me, |auftragnr: "| & auftragnr & |"|, Nothing) Call RethrowError() End Function
Sehen Sie den Unterschied? Ich nehme an, es ist leichter, nachdem ich Ihnen die Lösung verraten habe.
In der Document/literal Version muss die Zeile für den eigentlichen Aufruf des Web Services lauten Let CHECKOUT_STATUS = Service.Invoke("CHECKOUT_STATUS", auftragnr)
Zu beachten ist die Großschreibung bei CHECKOUT_STATUS.
Ich habe es herausgefunden, weil ich den zunächst generierten LotusScript Code um Fehlerbehandlungsroutinen ergänzt habe. Kein Code ist vollständig ohne Fehlerbehandlung. Das gilt insbesondere für die Verwendung von Web Services.
Da ich ein fauler Programmierer bin, habe ich den Code von einem Web Service Consumer zum anderen kopiert, ohne auf die Groß- und Kleinschreibung zu achten. Dem entsprechend war ich sehr erstaunt, als der eine Web Service Consumer ohne Probleme funktionierte und der zweite eine java.lang.NullPointerException zurück lieferte.
Nachdem ich diverse Fehlerursachen ausgeschlossen hatte, habe ich einen neuen Web Service Consumer generieren lassen. Dabei ist mir der Unterschied aufgefallen.
Die Ursache liegt wie oben schon angedeutet in der internen Verwendung von Java. Während des Aufrufes von Service.Invoke wird im Hintergrund die Java Reflection API verwendet, um die zugehörige Methode zu finden und aufzurufen. Die zwei unterschiedlichen WSDL Typen führten zu zwei unterschiedlichen Methodennamen: checkout_status und CHECKOUT_STATUS. In LotusScript kann die Groß- und Kleinschreibung vernachlässigt werden. In Java kann man Stunden damit verbringen, nach einem Fehler zu suchen, wenn man nicht darauf achtet.
Selbstverständlich wäre ich dem Problem von vorne herein aus dem Weg gegangen, wenn ich gleich Java für die Web Service Consumer bzw. Provider verwendet hätte.
Aber das war nicht die Aufgabe. ;-)