Here is a reproducible test (I am using ApplicationComposer from TomEE), I
replaced the repository by some other partial bean to simplify.
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.lang.annotation.Retention;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import javax.annotation.Resource;
import javax.enterprise.concurrent.ManagedThreadFactory;
import javax.inject.Inject;
import org.apache.deltaspike.core.api.provider.BeanManagerProvider;
import org.apache.deltaspike.partialbean.api.PartialBeanBinding;
import org.apache.deltaspike.partialbean.impl.PartialBeanBindingExtension;
import
org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler;
import org.apache.deltaspike.proxy.impl.invocation.InterceptorLookup;
import org.apache.openejb.junit.ApplicationComposerRule;
import org.apache.openejb.testing.CdiExtensions;
import org.apache.openejb.testing.Module;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
public @CdiExtensions({BeanManagerProvider.class,
PartialBeanBindingExtension.class}) class ConcurrencyBugTest {
public final @Rule TestRule composer = new ApplicationComposerRule(this);
public @PartialBeanBinding @Retention(RUNTIME) @interface
MyPartialBeanBinding {
}
public @MyPartialBeanBinding interface PartialBean {
String getValue();
}
public static @MyPartialBeanBinding class MyPartialBeanHandler implements
InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
return method.getName();
}
}
private class BlockPolicy implements RejectedExecutionHandler {
public @Override void rejectedExecution(Runnable runnable,
ThreadPoolExecutor executor) {
try {
executor.getQueue().put(runnable);
} catch (InterruptedException interruptedException) {
Thread.currentThread().interrupt();
}
}
}
public @Module Class<?>[] classes() {
return new Class<?>[] { DelegateManualInvocationHandler.class,
InterceptorLookup.class, PartialBean.class, MyPartialBeanHandler.class,
MyPartialBeanHandler.class };
}
private @Resource ManagedThreadFactory threadFactory;
private @Inject PartialBean bean;
public @Test void test() throws Exception {
ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, SECONDS,
new SynchronousQueue<>(), this.threadFactory, new BlockPolicy());
List<Future<String>> results = new ArrayList<>(100);
for (int i = 0; i < 100; i++) {
results.add(executor.submit(this.bean::getValue));
}
executor.shutdown();
executor.awaitTermination(60, SECONDS);
for (int i = 0; i < 100; i++) {
results.get(i).get();
}
}
}
java.util.concurrent.ExecutionException: java.lang.NullPointerException
at java.util.concurrent.FutureTask.report(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at
be.fgov.sfpd.attestation.ConcurrencyBugTest.test(ConcurrencyBugTest.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at
org.apache.openejb.junit.DeployApplication$1.call(DeployApplication.java:44)
at
org.apache.openejb.junit.DeployApplication$1.call(DeployApplication.java:40)
at
org.apache.openejb.testing.ApplicationComposers.evaluate(ApplicationComposers.java:1067)
at
org.apache.openejb.junit.DeployApplication.evaluate(DeployApplication.java:40)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.NullPointerException
at
org.apache.deltaspike.proxy.impl.invocation.AbstractManualInvocationHandler.invoke(AbstractManualInvocationHandler.java:38)
at
org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler$$OwbNormalScopeProxy0.invoke(org/apache/deltaspike/proxy/impl/invocation/DelegateManualInvocationHandler.java)
at
org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler.staticInvoke(DelegateManualInvocationHandler.java:40)
at
be.fgov.sfpd.attestation.ConcurrencyBugTest$PartialBean$$DSPartialBeanProxy.getValue(Unknown
Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
----------------------------------------
> From: [email protected]
> To: [email protected]
> Subject: RE: NPE in AbstractManualInvocationHandler
> Date: Wed, 13 Jul 2016 16:54:20 +0200
>
> I can reproduce the problem in a test (~4 times out of 5) with my whole
> application, if needed I can try to isolate the problem by reducing my app to
> the bare minimum.
> I am using your snapshots but, if needed, I can build it myself.
>
> Xavier
>
> ----------------------------------------
>> From: [email protected]
>> Date: Wed, 13 Jul 2016 14:46:29 +0000
>> Subject: Re: NPE in AbstractManualInvocationHandler
>> To: [email protected]
>>
>> Do you have a reproducible sequence or is it only under load? I think I
>> know the issue. Also did you build 1.7.1 yourself or pointing to our
>> snapshots?
>>
>> John
>>
>> On Wed, Jul 13, 2016 at 10:41 AM Xavier Dury <[email protected]> wrote:
>>
>>> Sorry,
>>>
>>> I spoke too soon, I still have the problem:
>>>
>>> Caused by: java.lang.NullPointerException
>>> at
>>> org.apache.deltaspike.proxy.impl.invocation.AbstractManualInvocationHandler.invoke(AbstractManualInvocationHandler.java:38)
>>> at
>>> org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler$$OwbNormalScopeProxy0.invoke(org/apache/deltaspike/proxy/impl/invocation/DelegateManualInvocationHandler.java)
>>> at
>>> org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler.staticInvoke(DelegateManualInvocationHandler.java:40)
>>>
>>> Xavier
>>>
>>> ----------------------------------------
>>>> From: [email protected]
>>>> To: [email protected]
>>>> Subject: RE: NPE in AbstractManualInvocationHandler
>>>> Date: Wed, 13 Jul 2016 16:36:18 +0200
>>>>
>>>> Hi,
>>>>
>>>> Indeed, 1.7.1-SNAPSHOT fixed my problem.
>>>>
>>>> Thanks,
>>>>
>>>> Xavier
>>>> ----------------------------------------
>>>>> From: [email protected]
>>>>> Date: Wed, 13 Jul 2016 14:23:43 +0000
>>>>> Subject: Re: NPE in AbstractManualInvocationHandler
>>>>> To: [email protected]
>>>>>
>>>>> Hi Xavier,
>>>>>
>>>>> Could you try with 1.7.1-snapshot? There was some weird static logic
>>> doing
>>>>> initialization which has been changed in the up coming 1.7.1
>>>>>
>>>>> John
>>>>>
>>>>> On Wed, Jul 13, 2016 at 9:49 AM Xavier Dury <[email protected]> wrote:
>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> Since I upgraded to DeltaSpike 1.7.0 / TomEE 7.0.1, I get NPEs when
>>>>>> calling some repositories in a multi-threaded batch.
>>>>>>
>>>>>> Caused by: java.lang.NullPointerException
>>>>>> at
>>>>>>
>>> org.apache.deltaspike.proxy.impl.invocation.AbstractManualInvocationHandler.invoke(AbstractManualInvocationHandler.java:40)
>>>>>> at
>>>>>>
>>> org.apache.deltaspike.proxy.impl.invocation.DelegateManualInvocationHandler.staticInvoke(DelegateManualInvocationHandler.java:39)
>>>>>>
>>>>>> That line contains the following code:
>>>>>>
>>>>>> List<Interceptor<?>> interceptors = interceptorLookup.lookup(proxy,
>>>>>> method);
>>>>>>
>>>>>> So it seems that interceptorLookup was not initialized correctly.
>>>>>>
>>>>>> I see there is a double-check locking on a volatile Boolean for
>>>>>> initialization in that class... Wouldn't it be better if the locking
>>>>>> occurred on the interceptorLookup or if the interceptorLookup would
>>> also be
>>>>>> declared as volatile?
>>>>>>
>>>>>> Regards,
>>>>>>
>>>>>> Xavier
>>>>>>
>>>>
>>>
>