Recently I worked on a demo application for a customer to show how to use Web Service Consumer in LotusScript. At one point I got a java.lang.NullPointerException
error. It took me quite some time to figure out the reason for this error. So this blog post should help anyone how might run in the same problem. Because frankly the solution was really simple once I had found it.
I give you the summary first and the full technical explanation later in this blog post. So even if you are not interested in the details the main point to take away is:
LotusScript Web Services Consumer and Provider use internally Java and Java is case sensitive. If you do not obey this you might loose some hours of your life time chasing an error where no error is.
Here are the technical details.
One part of the demo were the different styles to use to generate the WSDL file from a Web Service Provider. There is a great article from IBM developerWorks Which style of WSDL should I use? on this topic. Highly recommended!
To be able to show the differences between RPC/encoded and Document/literal style I implemented two different Web Service Providers. Both providers had the same identical LotusScript code.
Then I implemented two Web Service Consumers just by importing the WSDL file from the previous generated Web Service Providers. Here are the two methods from the Web Service Consumers.
Web Service Consumer calling a 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 calling a 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
Can you spot the difference? I guess it is easier after giving you the solution.
In the Document/literal version the line for actually calling the Web Service must be Let CHECKOUT_STATUS = Service.Invoke("CHECKOUT_STATUS", auftragnr)
with capital CHECKOUT_STATUS.
I found out because I took the original generated code and enhanced it with some error handling. Since there could be not code without properly error handling. This is specially true for calling Web Service.
Since I'm a lazy programmer I copied the code from the one Web Service Consumer to the next not looking at the case. I was very confusing that the first Web Service Consumer worked and the second one returned a java.lang.NullPointerException.
After checking for all kind of reasons I finally started again with a new Web Service Consumer and then found the difference in the case.
The reason behind is the internal use of Java. While calling Service.Invoke in the background the Java Reflection API is used to find the corresponding method to call. Those two different WSDL styles lead to two different method names: checkout_status and CHECKOUT_STATUS. In LotusScript the case of a method name makes no difference. In Java it can mean you can spend hours looking for an error.
By the way if I had used Java in the first place to program the Web Service Consumer (or Provider) I would not have had this problem.