Project

General

Profile

Bug #2136

Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed

Added by Udo Offermann about 2 years ago. Updated about 2 years ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
z2-base
Target version:
-
Start date:
31.01.2022
Due date:
% Done:

0%

Estimated time:
origin:
#1

Updated by Udo Offermann about 2 years ago

  • Subject changed from com.zfabrik.impl.db.data.DataSourceWorkResource#DataSourceWorkResource throws NPE on "isClosed" when connextion is already closed to Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed
#2

Updated by Udo Offermann about 2 years ago

Während der Ausführung des MTS Demo-Szenarios tritt folgender Fehler reproduzierbar auf:

01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]: java.lang.NullPointerException
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at sun.reflect.GeneratedMethodAccessor65.invoke(Unknown Source)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at java.lang.reflect.Method.invoke(Method.java:498)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.db.data.DataSourceWorkResource.lambda$0(DataSourceWorkResource.java:44)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.sun.proxy.$Proxy162.isClosed(Unknown Source)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.releaseConnection(LogicalConnectionManagedImpl.java:214)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.afterTransaction(LogicalConnectionManagedImpl.java:175)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.afterTransaction(JdbcCoordinatorImpl.java:275)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.afterTransactionCompletion(JdbcCoordinatorImpl.java:454)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.afterCompletion(JtaTransactionCoordinatorImpl.java:384)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.doAfterCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:60)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl.afterCompletion(SynchronizationCallbackCoordinatorTrackingImpl.java:72)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.afterCompletion(RegisteredSynchronization.java:44)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.tx.TransactionImpl$Resource.afterCompletion(TransactionImpl.java:124)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit._afterCompletion(WorkUnit.java:434)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.lambda$_afterCompletion$3(WorkUnit.java:429)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.visitResources(WorkUnit.java:486)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit._afterCompletion(WorkUnit.java:429)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.commit(WorkUnit.java:224)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.tx.TransactionImpl.commit(TransactionImpl.java:229)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.tx.UserTransactionImpl.commit(UserTransactionImpl.java:96)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1035)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(AbstractTransactionAspect.aj:71)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.mts.workq.impl.service.TaskExecutionServiceImpl.progressTaskTX(TaskExecutionServiceImpl.java:249)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.mts.workq.impl.service.TaskExecutionServiceImpl.progressTask(TaskExecutionServiceImpl.java:174)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.mts.workq.impl.job.TaskJobImpl.execute(TaskJobImpl.java:38)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.tt.scheduler.JobDelegate.lambda$1(JobDelegate.java:145)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.util.threading.ThreadUtil.lambda$cleanContextExceptionExecute$0(ThreadUtil.java:55)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at java.security.AccessController.doPrivileged(Native Method)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.util.threading.ThreadUtil.cleanContextExceptionExecute(ThreadUtil.java:55)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.tt.scheduler.JobDelegate.lambda$0(JobDelegate.java:140)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.lambda$work$1(WorkUnit.java:396)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.supply(WorkUnit.java:368)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.work.WorkUnit.work(WorkUnit.java:396)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.work.ThreadPoolImpl.doIt(ThreadPoolImpl.java:273)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.zfabrik.impl.work.ThreadPoolImpl.executeAs(ThreadPoolImpl.java:359)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.tt.scheduler.JobDelegate.executeWrappedJob(JobDelegate.java:123)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.tt.scheduler.JobDelegate.execute(JobDelegate.java:55)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at com.gi_de.tt.jobs.ComponentJob.execute(ComponentJob.java:34)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]:  at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
01/31 11:01:42 [24]...ent/jobWorker@0.2 [800]: 
0

Das Problem ist, dass com.zfabrik.impl.db.data.DataSourceWorkResource#DataSourceWorkResource#close() den Member conn=null setzt (im Finally-Block), danach aber noch Aufrufe - wie z.B. isClosed() auf der Connection per Reflection zulässt bzw. selbst aufruft. dadurch wird die NPE im Connection-Wrapper ausgelöst, der als Proxy im Konstruktor aufgebaut wird:

        this.wrapper = (Connection) Proxy.newProxyInstance(
            this.getClass().getClassLoader(), 
            new Class<?>[]{Connection.class}, 
            (Object proxy, Method method, Object[] args) -> {
                if (!"close".equals(method.getName())) {
NPE --->            return method.invoke(DataSourceWorkResource.this.conn, args);
                } else {
                    // if anybody set the connection to auto commit, 
                    // we set it back to no-autocommit
                    if (conn.getAutoCommit()) {
                        LOG.warning("Setting pool connection back to non-autocommit");
                        conn.setAutoCommit(false);
                    }
                }
                return null;
            }
        );

Lösung

Der Proxy muss für method== isClosed true zurückliefern, wenn this.conn==null ist.
An dieser Stelle sollte auch für den Fall method close auf this.connnull getestet werden.

#3

Updated by Udo Offermann about 2 years ago

  • Category set to z2-base

In MTS wurde das Problem durch folgende Änderung im Konstruktor von com.zfabrik.impl.db.data.DataSourceWorkResource gelöst:

   public DataSourceWorkResource(DataSourceResource dataSourceResource, final Connection connection) throws Exception {
        this.dataSourceResource = dataSourceResource;
        this.conn = connection;

        // Delegate the calls to the wrapped connection except the close method which will be handled separately.
        this.wrapper = (Connection) Proxy.newProxyInstance(
                this.getClass().getClassLoader(),
                new Class<?>[]{Connection.class},
                (Object proxy, Method method, Object[] args) -> {
                    switch (method.getName()) {
                        case "isClosed":
                            // delegate "isClosed", unless we are already closed
                            return this.conn == null || this.conn.isClosed();

                        case "close":
                            if (this.conn != null) {
                                // if anybody set the connection to auto commit,
                                // we set it back to no-autocommit
                                if (this.conn.getAutoCommit()) {
                                    LOG.warning("Setting pool connection back to non-autocommit");
                                    this.conn.setAutoCommit(false);
                                }
                            }
                            break;

                        default:
                            if (this.conn != null) {
                                // delegate methods
                                return method.invoke(DataSourceWorkResource.this.conn, args);
                            } else {
                                // This is a preferred behavior based on the java.sql.Connection documentation.
                                throw new SQLException("Connection already closed");
                            }
                    }
                    return null;
                }
        );
    }

Also available in: Atom PDF