https://redmine.z2-environment.net/
https://redmine.z2-environment.net/favicon.ico?1581355187
2022-01-31T11:56:03Z
z2-Environment
z2-Environment - Bug #2136: Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed
https://redmine.z2-environment.net/issues/2136?journal_id=4589
2022-01-31T11:56:03Z
Udo Offermann
udo.offermann@zfabrik.de
<ul><li><strong>Subject</strong> changed from <i>com.zfabrik.impl.db.data.DataSourceWorkResource#DataSourceWorkResource throws NPE on "isClosed" when connextion is already closed</i> to <i>Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed</i></li></ul>
z2-Environment - Bug #2136: Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed
https://redmine.z2-environment.net/issues/2136?journal_id=4590
2022-01-31T12:07:34Z
Udo Offermann
udo.offermann@zfabrik.de
<ul></ul><p>Während der Ausführung des MTS Demo-Szenarios tritt folgender Fehler reproduzierbar auf:</p>
<pre>
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
</pre>
<p>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:</p>
<pre><code class="java syntaxhl"> <span class="k">this</span><span class="o">.</span><span class="na">wrapper</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Connection</span><span class="o">)</span> <span class="nc">Proxy</span><span class="o">.</span><span class="na">newProxyInstance</span><span class="o">(</span>
<span class="k">this</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getClassLoader</span><span class="o">(),</span>
<span class="k">new</span> <span class="nc">Class</span><span class="o"><?>[]{</span><span class="nc">Connection</span><span class="o">.</span><span class="na">class</span><span class="o">},</span>
<span class="o">(</span><span class="nc">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="nc">Method</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="s">"close"</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">()))</span> <span class="o">{</span>
<span class="no">NPE</span> <span class="o">---></span> <span class="k">return</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="nc">DataSourceWorkResource</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">conn</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="c1">// if anybody set the connection to auto commit, </span>
<span class="c1">// we set it back to no-autocommit</span>
<span class="k">if</span> <span class="o">(</span><span class="n">conn</span><span class="o">.</span><span class="na">getAutoCommit</span><span class="o">())</span> <span class="o">{</span>
<span class="no">LOG</span><span class="o">.</span><span class="na">warning</span><span class="o">(</span><span class="s">"Setting pool connection back to non-autocommit"</span><span class="o">);</span>
<span class="n">conn</span><span class="o">.</span><span class="na">setAutoCommit</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">);</span>
</code></pre>
<a name="Lösung"></a>
<h3 >Lösung<a href="#Lösung" class="wiki-anchor">¶</a></h3>
<p>Der Proxy muss für method== isClosed true zurückliefern, wenn this.conn==null ist.<br />An dieser Stelle sollte auch für den Fall method close auf this.connnull getestet werden.</p>
z2-Environment - Bug #2136: Z2-DataSourceWorkResource throws NPE on "isClosed" when connection is already closed
https://redmine.z2-environment.net/issues/2136?journal_id=4591
2022-01-31T13:08:33Z
Udo Offermann
udo.offermann@zfabrik.de
<ul><li><strong>Category</strong> set to <i>z2-base</i></li></ul><p>In MTS wurde das Problem durch folgende Änderung im Konstruktor von <code>com.zfabrik.impl.db.data.DataSourceWorkResource</code> gelöst:</p>
<pre><code class="java syntaxhl"> <span class="kd">public</span> <span class="nf">DataSourceWorkResource</span><span class="o">(</span><span class="nc">DataSourceResource</span> <span class="n">dataSourceResource</span><span class="o">,</span> <span class="kd">final</span> <span class="nc">Connection</span> <span class="n">connection</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">dataSourceResource</span> <span class="o">=</span> <span class="n">dataSourceResource</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">conn</span> <span class="o">=</span> <span class="n">connection</span><span class="o">;</span>
<span class="c1">// Delegate the calls to the wrapped connection except the close method which will be handled separately.</span>
<span class="k">this</span><span class="o">.</span><span class="na">wrapper</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Connection</span><span class="o">)</span> <span class="nc">Proxy</span><span class="o">.</span><span class="na">newProxyInstance</span><span class="o">(</span>
<span class="k">this</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getClassLoader</span><span class="o">(),</span>
<span class="k">new</span> <span class="nc">Class</span><span class="o"><?>[]{</span><span class="nc">Connection</span><span class="o">.</span><span class="na">class</span><span class="o">},</span>
<span class="o">(</span><span class="nc">Object</span> <span class="n">proxy</span><span class="o">,</span> <span class="nc">Method</span> <span class="n">method</span><span class="o">,</span> <span class="nc">Object</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">-></span> <span class="o">{</span>
<span class="k">switch</span> <span class="o">(</span><span class="n">method</span><span class="o">.</span><span class="na">getName</span><span class="o">())</span> <span class="o">{</span>
<span class="k">case</span> <span class="s">"isClosed"</span><span class="o">:</span>
<span class="c1">// delegate "isClosed", unless we are already closed</span>
<span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">conn</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="k">this</span><span class="o">.</span><span class="na">conn</span><span class="o">.</span><span class="na">isClosed</span><span class="o">();</span>
<span class="k">case</span> <span class="s">"close"</span><span class="o">:</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">conn</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// if anybody set the connection to auto commit,</span>
<span class="c1">// we set it back to no-autocommit</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">conn</span><span class="o">.</span><span class="na">getAutoCommit</span><span class="o">())</span> <span class="o">{</span>
<span class="no">LOG</span><span class="o">.</span><span class="na">warning</span><span class="o">(</span><span class="s">"Setting pool connection back to non-autocommit"</span><span class="o">);</span>
<span class="k">this</span><span class="o">.</span><span class="na">conn</span><span class="o">.</span><span class="na">setAutoCommit</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="k">if</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">conn</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// delegate methods</span>
<span class="k">return</span> <span class="n">method</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span><span class="nc">DataSourceWorkResource</span><span class="o">.</span><span class="na">this</span><span class="o">.</span><span class="na">conn</span><span class="o">,</span> <span class="n">args</span><span class="o">);</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="c1">// This is a preferred behavior based on the java.sql.Connection documentation.</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">SQLException</span><span class="o">(</span><span class="s">"Connection already closed"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre>