QuickBooks NOT working on Google App Engine

2020-04-28 09:23发布

问题:

I was trying to connect to quickbooks online from google app engine by just running this simple code:

OAuthCredentials oauthcredentials = new OAuthCredentials(consumerKey, consumerSecret, accessToken, accessTokenSecret);

PlatformSessionContext context = new PlatformSessionContext(oauthcredentials, appToken, PlatformServiceType.QBO, realmID);

QBCustomerService customerService = QBServiceFactory.getService(context, QBCustomerService.class);

QBIdType idType = new QBIdType(context);

idType.setValue("2");

QBCustomer c = customerService.findById(context, idType);

System.err.println(c.getName());

and it always throws me this error:

com.intuit.ds.qb.IDSException: java.net.SocketException: Operation failure: Unknown error.
    at com.google.appengine.api.socket.SocketApiHelper.translateError(SocketApiHelper.java:105)
    at com.google.appengine.api.socket.SocketApiHelper.translateError(SocketApiHelper.java:118)
    at com.google.appengine.api.socket.SocketApiHelper.makeSyncCall(SocketApiHelper.java:82)
    at com.google.appengine.api.socket.AppEngineSocketImpl.setSocketOptionAsBytes(AppEngineSocketImpl.java:229)
    at com.google.appengine.api.socket.AppEngineSocketOptions$BooleanCheckFunction.apply(AppEngineSocketOptions.java:168)
    at com.google.appengine.api.socket.AppEngineSocketOptions$Option.validateAndApply(AppEngineSocketOptions.java:358)
    at com.google.appengine.api.socket.AppEngineSocketOptions$Option.validateAndApply(AppEngineSocketOptions.java:373)
    at com.google.appengine.api.socket.AppEngineSocketImpl.setOption(AppEngineSocketImpl.java:144)
    at com.google.appengine.api.socket.AppEngineSocketImpl.setOption(AppEngineSocketImpl.java:171)
    at java.net.Socket.setTcpNoDelay(Socket.java:950)
    at sun.security.ssl.BaseSSLSocketImpl.setTcpNoDelay(BaseSSLSocketImpl.java:330)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.prepareSocket(DefaultClientConnectionOperator.java:222)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:153)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
    at com.intuit.ds.qb.IDSRestClient.sendRequest(IDSRestClient.java:557)
    at com.intuit.ds.qb.IDSRestClient.get(IDSRestClient.java:160)
    at com.intuit.ds.qb.qbo.QBOService.getQBOBaseURI(QBOService.java:24)
    at com.intuit.ds.qb.IDSRestClient.setServiceTypeProperty(IDSRestClient.java:698)
    at com.intuit.ds.qb.qbo.QBORequestCreator.doIDSGet(QBORequestCreator.java:41)
    at com.intuit.ds.qb.qbo.AbstractQBOBaseDataService.findById(AbstractQBOBaseDataService.java:185)
    at com.intuit.ds.qb.qbo.QBOCustomerService.findById(QBOCustomerService.java:153)
    at com.xyz.server.tasks.SyncCustomerTask.runMethod(SyncCustomerTask.java:85)
    at com.xyz.server.tasks.SyncCustomerTask.run(SyncCustomerTask.java:33)
    at com.google.apphosting.utils.servlet.DeferredTaskServlet.performRequest(DeferredTaskServlet.java:136)
    at com.google.apphosting.utils.servlet.DeferredTaskServlet.service(DeferredTaskServlet.java:99)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:57)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:266)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:146)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:438)
    at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:480)
    at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:487)
    at com.google.tracing.TraceContext.runInContext(TraceContext.java:774)
    at com.google.tracing.TraceContext$DoInTraceContext.runInContext(TraceContext.java:751)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:342)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:334)
    at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:484)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:251)
    at java.lang.Thread.run(Thread.java:722)

where at com.xyz.server.tasks.SyncCustomerTask.runMethod(SyncCustomerTask.java:85) is the findbyId("2") line in my code. Any suggestions how to get around this issue? Any help will be really really appriciated

回答1:

AppEngine provides only a limited Socket API. In particular, you cannot use most getsockopt/setsockopt calls, which in your case throw the error (setTcpNoDelay(..)).

QuickBooks SDK seems to be closed-source, so there is no way around it.

Update:

As of GAE Java SDK 1.8.1., it's possible to use socket options methods: they are a no-op, but they do not throw exceptions as they used to:

The Sockets API now allows client code to call get/set options against sockets. Previously, calls raised "Not Implemented" exceptions. When java.net.Socket.get<option>() is called, a mock value is returned, calls to set<option>() will be silently ignored.



回答2:

I am SO thankful that you posted this question. I had been banging my head against the wall for days with the same problem, except I am developing my Google App in the Go language, not Java. BUT I MADE IT WORK! I hope that my solution will help you also. Basically, instead of using the sockets service, I am using the URL Fetch service, documented for Java here: https://developers.google.com/appengine/docs/java/urlfetch/

I am pretty clueless about some of the details in your original post (I have not developed in Java, and am not familiar with the QBServiceFactory nor the OAuthCredentials). So I am not sure how much control you have over how it actually sends the requests to QB. But worst case scenario, I think you may be able to write your own functions to replace the QBServiceFactory (which I have had to do in Go), and if you make sure they use the URL Fetch service instead of sockets, you may have better luck.

As I said, I am very thankful for your question, because I do not think that I would have stumbled across my answer otherwise, so thank you!