moqui / moqui-framework

Use Moqui Framework to build enterprise applications based on Java. It includes tools for databases (relational, graph, document), local and web services, web and other UI with screens and forms, security, file/resource access, scripts, templates, l10n, caching, logging, search, rules, workflow, multi-instance, and integration.
http://www.moqui.org
Other
284 stars 204 forks source link

NullPointerException error while running asynchronous and distributed services #510

Closed igorgallingani closed 2 years ago

igorgallingani commented 2 years ago

During a distributed asynchronous call, via Hazelcast, the instruction at line 129

....
if (ecfi.transactionFacade.isTransactionInPlace ()) {
....

raises a NullPointerException. Here is the suggested change to the runInternal method:

Map <String, Object> runInternal () throws Exception {
            ExecutionContextImpl threadEci = (ExecutionContextImpl) null
            try {
                ecfi = getEcfi ()
                // check for active Transaction
                if (ecfi.transactionFacade.isTransactionInPlace ()) {
                    logger.error ("In ServiceCallAsync service $ {serviceName} a transaction is in place for thread $ {Thread.currentThread (). getName ()}, trying to commit")
                    try {
                        ecfi.transactionFacade.destroyAllInThread ()
                    } catch (Exception e) {
                        logger.error ("ServiceCallAsync commit in place transaction failed for thread $ {Thread.currentThread (). getName ()}", e)
                    }
                }
                // check for active ExecutionContext
                ExecutionContextImpl activeEc = ecfi.activeContext.get ()
                if (activeEc! = null) {
                    logger.error ("In ServiceCallAsync service $ {serviceName} there is already an ExecutionContext for user $ {activeEc.user.username} (from $ {activeEc.forThreadId}: $ {activeEc.forThreadName}) in this thread $ {Thread. currentThread (). id}: $ {Thread.currentThread (). name}, destroying ")
                    try {
                        activeEc.destroy ()
                    } catch (Throwable t) {
                        logger.error ("Error destroying ExecutionContext already in place in ServiceCallAsync in thread $ {Thread.currentThread (). id}: $ {Thread.currentThread (). name}", t)
                    }
                }

                threadEci = ecfi.getEci ()
                if (threadUsername! = null && threadUsername.length ()> 0)
                    threadEci.userFacade.internalLoginUser (threadUsername, false)

                // NOTE: authz is disabled because authz is checked before queuing
                Map <String, Object> result = threadEci.serviceFacade.sync (). Name (serviceName) .parameters (parameters) .disableAuthz (). Call ()
                return result
            } catch (Throwable t) {
                logger.error ("Error in async service", t)
                throw t
            } finally {
                if (threadEci! = null) threadEci.destroy ()
            }
        }
jonesde commented 2 years ago

With what is here so far this issue would require a lot of work to attempt to reproduce because there are no steps to reproduce, without a stacktrace it would be difficult to know where the NPE happened in the existing code, and with a code snippet that includes formatting changes rather than something like a patch it is a lot of work to figure out what the changes you made are.

Without more information this could easily take an hour to reproduce and track down... with a risk that there is some nuance to your scenario that is not stated here and that effort in reproducing it would fail, making it a waste of time.

With more information, and information in a more consumable form where you are not asking me or others to do so much work, this could be a matter of a few minutes to review and fix.

igorgallingani commented 2 years ago

Sorry David, I ran the moqui-cluster1-compose.yml with the 'moqui' image prepared with the moqui-hazelcast component. The variable ecfi on line 129 is null during a distributed invocation of the services. Initializing it with ecfi = getEcfi () at the beginning of the method solves the problem.

jonesde commented 2 years ago

I think I see what you mean, in the ServiceCallAsyncImpl.AsyncServiceInfo inner class the way it handles the local/cached ecfi field versus the getEcfi() method did not guarantee that it always called the method. Changes to do that are in commit 0a78537c.

If that resolves the issue you're running into please close this issue.