问题
(Yes, I realize that this question is similar to another question posted; however, that question was never answered.)
Objective
I'm trying to use Clojure with Axis 2 to access QuickBooks data using the QuickBooks WebConnector (QBWC from now on). QBWC uses SOAP to communicate with external applications, so that's why my Clojure application uses Axis 2 as a SOAP interface. I use micha's clj-soap from GitHub to provide higher-level (abstracted) Clojure interop with the Java calls of Axis 2.
Issue
The issue is in authenticating my SOAP web service with QBWC. With the authenticate
method of my SOAP web service, QBWC throws an error. In the QBWC log file it says the following:
QBWebConnector.SOAPWebService.do_authenticate() : QBWC1012: Authentication failed due to following error message: Object reference not set to an instance of an object.
StackTrace = at QBWebConnector.WebService.do_authenticate(String& ticket, String& companyFileName)
Source = QBWebConnector
Code
Below is the Clojure code that defines a web service called SOCAccess for the Axis 2 server I'm using.
(For those unfamiliar with Clojure but familiar with Java, the following code creates a class called developer.intuit.com.SOCAccess
and defines a number of methods which are needed to communicate with QBWC (including authenticate
, serverVersion
, etc.). The carets indicate the return or parameter type. Clojure compiles into Java bytecode and by default it doesn't use strong typing, which translates into most type hints being Object
, but because of the way Java2WSDL works, strong typing is required. For instance:
Type ^String
ends up as <xs:element minOccurs="0" name="return" nillable="true" type="xs:string"/>
Type ^Object
ends up as <xs:element minOccurs="0" name="return" nillable="true" type="xs:anyType"/>
Type ^"[Ljava.lang.String;"
is the same thing as String[]
, and ends up as <xs:element maxOccurs="unbounded" minOccurs="0" name="return" nillable="true" type="xs:string"/>
)
(soap/defservice developer.intuit.com.SOCAccess
(authenticate2 ^"[Ljava.lang.String;" [^String username ^String password]
(authenticate2* username password))
(authenticate ^"[Ljava.lang.String;" [^String username ^String password]
(authenticate* username password))
(prefix35authenticate ^"[Ljava.lang.String;" [^String username ^String password]
(authenticate* username password))
(sendRequestXML ^Object
[^String ticket ^String hcp-response ^String company-file-name ^String qb-xml-country
^Integer qb-xml-major-vers, ^Integer qb-xml-minor-vers]
(send-request-xml ticket hcp-response company-file-name qb-xml-country qb-xml-major-vers qb-xml-minor-vers))
(receiveResponseXML ^Integer [^String ticket ^String response ^String hresult ^String message]
(receive-response-xml ticket response hresult message))
(connectionError ^Object [^String ticket ^String hresult ^String message]
(connection-error ticket hresult message))
(getLastError ^String [^String ticket]
(get-last-error ticket))
(closeConnection ^String [^String ticket]
(close-connection ticket))
(getServerVersion ^String [^String ticket] (server-version ticket))
(serverVersion ^String [^String ticket] (server-version ticket))
(clientVersion ^String [^String version] (client-version version))
(interactiveDone ^String [^String ticket] (interactive-done ticket))
(interactiveRejected ^String [^String ticket ^String reason] (interactive-rejected ticket reason)))
Server Initialization
The output window prints the following once I start an AxisServer
and call .addService
using the class name "developer.intuit.com.SOCAccess"
, which is a class compiled from the code above:
Jul 07, 2014 11:22:20 AM org.apache.axis2.transport.http.server.DefaultConnectionListener run
INFO: Listening on port 6060
And everything appears to be running normally.
SOAP Calls
However, when I make the SOAP call (authenticate "my-username" "my-password")
to my web service within the JVM I'm running, the return value is supposed to be a String[]
and the println() output shows that it is indeed a String[]
but the return value is shown as only being the first element of the array, which is strange. My guess is that this is the cause of the Object reference not set to an instance of an object
error from QBWC, because it's trying to get the nth element of a String
as if it were an array.
========
Any thoughts? I realize this is a long question. Let me know what else you would like me to provide in the way of code, WSDLs, etc.
Thanks!
ADDENDUM: LOG FILE
20140707.20:06:40 UTC : QBWebConnector.WebServiceManager.DoUpdateSelected() : updateWS() for application = 'SOCAccess' has STARTED
20140707.20:06:40 UTC : QBWebConnector.RegistryManager.getUpdateLock() : HKEY_CURRENT_USER\Software\Intuit\QBWebConnector\UpdateLock = FALSE
20140707.20:06:40 UTC : QBWebConnector.RegistryManager.setUpdateLock() : HKEY_CURRENT_USER\Software\Intuit\QBWebConnector\UpdateLock has been set to True
20140707.20:06:40 UTC : QBWebConnector.RegistryManager.setUpdateLock() : ********************* Update session locked *********************
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.instantiateWebService() : Initiated connection to the following application.
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.instantiateWebService() : AppName: SOCAccess
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.instantiateWebService() : AppUniqueName (if available): SOCAccess
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.instantiateWebService() : AppURL: http://localhost:6060/axis2/services/SOCAccess
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_serverVersion() : *** Calling serverVersion().
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_serverVersion() : Received from serverVersion() following parameter:<serverVersionRet="">
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_serverVersion() : This application sent a null for server version. Allowing update operation.
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_clientVersion() : *** Calling clientVersion() with following parameter:<productVersion="2.1.0.27">
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_clientVersion() : Received from clientVersion() following parameter:<clientVersionRet="">
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_clientVersion() : This application agrees with the current version of QBWebConnector. Allowing update operation.
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_authenticate() : Authenticating to application 'SOCAccess', username = 'alexandergunnarson'
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_authenticate() : *** Calling authenticate() with following parameters:<userName="alexandergunnarson"><password=<MaskedForSecurity>
20140707.20:06:40 UTC : QBWebConnector.SOAPWebService.do_authenticate() : QBWC1012: Authentication failed due to following error message.
Object reference not set to an instance of an object.
More info:
StackTrace = at QBWebConnector.WebService.do_authenticate(String& ticket, String& companyFileName)
Source = QBWebConnector
20140707.20:06:40 UTC : QBWebConnector.RegistryManager.setUpdateLock() : HKEY_CURRENT_USER\Software\Intuit\QBWebConnector\UpdateLock has been set to False
20140707.20:06:40 UTC : QBWebConnector.RegistryManager.setUpdateLock() : ********************* Update session unlocked *********************
20140707.20:06:40 UTC : QBWebConnector.WebServiceManager.DoUpdateSelected() : Update completed with errors. See log (QWClog.txt) for details.
ADDENDUM: SOAP IN
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soapenv:Body>
<axis2ns34:authenticate xmlns:axis2ns34=\"http://developer.intuit.com/\">
<axis2ns35:args0 xmlns:axis2ns35=\"http://developer.intuit.com/\">
alexandergunnarson
</axis2ns35:args0>
<axis2ns36:args1 xmlns:axis2ns36=\"http://developer.intuit.com/\">
password
</axis2ns36:args1>
</axis2ns34:authenticate>
</soapenv:Body>
</soapenv:Envelope>
ADDENDUM: SOAP OUT
<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soapenv:Body>
<ns:authenticateResponse xmlns:ns=\"http://developer.intuit.com/\">
<return>my-session-token</return>
<return>none</return>
<return>60</return>
<return>60</return>
</ns:authenticateResponse>
</soapenv:Body>
</soapenv:Envelope>
UPDATE
Lots of things have happened since last I posted.
1) I tried to implement ArrayOfString, but that didn't integrate into the WSDL well.
2) I downloaded the QuickBooks WSDL from the Intuit website and used WSDL2Java (in Axis2) to generate a megalithic (10K LOC) .java file called QBWebConnectorSvcStub.java.
3) I tried extending the class and overriding the methods, but I kept getting lots of exceptions/errors.
4) I then tried editing the source code of the QBWebConnectorSvcStub
class, specifically focusing on certain methods (editing authenticate
, serverVersion
, and clientVersion
first because that's what QBWC first calls). For these methods, I tied the return values to functions in a Clojure class, passing the input of the QBWebConnectorSvcStub methods to the corresponding Clojure functions and returning the output of said functions. Basically I had the Clojure functions do the work and QBWebConnectorSvcStub
act as a SOAP go-between.
5) At this point, I'm back to the same error as before (although blessedly, clientVersion
works as expected, as evidenced by the log: Received from clientVersion() following parameter:<clientVersionRet="">
:
QBWebConnector.SOAPWebService.do_authenticate() : Authenticating to application 'QBWebConnectorSvcStub', username = 'alexandergunnarson'
QBWebConnector.SOAPWebService.do_authenticate() : *** Calling authenticate() with following parameters:<userName="alexandergunnarson"><password=<MaskedForSecurity>
QBWebConnector.SOAPWebService.do_authenticate() : QBWC1012: Authentication failed due to following error message.
Object reference not set to an instance of an object.
The SOAP output to QuickBooks for this is:
<ns:authenticateResponse xmlns:ns="http://developer.intuit.com/">
<return xmlns:ax22="http://io.java/xsd" xmlns:ax21="http://rmi.java/xsd" xmlns:ax25="http://developer.intuit.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ax25:QBWebConnectorSvcStub_AuthenticateResponse">
<authenticateResult>
<string xmlns="http://developer.intuit.com/">
my-session-token
</string>
<string xmlns="http://developer.intuit.com/">
nvu
</string>
<string xmlns="http://developer.intuit.com/">
60
</string>
<string xmlns="http://developer.intuit.com/">
60
</string>
</authenticateResult>
<authenticateResultSpecified>
true
</authenticateResultSpecified>
</return>
</ns:authenticateResponse>
Luckily (!) there is now only one return value, unlike before.
Having taken a look at the example working authenticate SOAP request, perhaps there's still an issue because I took out the SOAP envelope/headers? They were giving me problems with nullPointerExceptions and such. I'll try to put them back in.
FINAL WORD
The next installment of this painful adventure is now here.
回答1:
I am not sure what your QBC file looks like (the xml file where your app is defined for Quickbooks Web Connector), but I fixed a similar problem by changing the Style setting from
<Style>RPC</Style>
to
<Style>DocWrapped</Style>
You may want to try the 'DocWrapped' or 'RPC' values if it is not defined. Details for Style from the QBWC_proguide:
The SOAP encoding style used by your web service. If not supplied, the default used is Document. Document is the standard encoding style used by .NET when the [WebMethod] attribute is applied to a function declaration. Optionally, you can specify the value DocWrapped. DocWrapped interoperates very well with Axis web services that are built as we recommend, using WSDL2Java to generate Java web classes from the standard WSDL used by the web connector (http:// developer.intuit.com/uploadedFiles/Support/ QBWebConnectorSvc.wsdl) Or, optionally, you can specify the value RPC. The RPC style is the standard encoding style used by Axis when a Java class is automatically converted to a SOAP service either through JWS or the Java2WSDL tool.
来源:https://stackoverflow.com/questions/24618311/authentication-issue-using-quickbooks-web-connector-object-reference-not-set-to