[
https://issues.jboss.org/browse/WELD-862?page=com.atlassian.jira.plugin.s...
]
Marius Bogoevici updated WELD-862:
----------------------------------
Issue Type: Enhancement (was: Bug)
OK, a few comments about this issue. I set it as an Enhancement, since this is not
something supported by the specification.
The interceptor code and the intercepted method must execute in the same call stack, which
is not the case here. The specification itself leaves little room for supporting a call to
proceed() from outside the original call stack because an InvocationContext is supposed to
be a mutable, stateful instance (since according to the specification, the *same* instance
must be passed along the chain of interceptors). At the same time, interceptors must
support recovery, which means that subsequent calls to proceed() will execute the whole
interceptor chain again, as in the following example:
{code}
try
{
ctx.proceed();
}
catch (Exception e)
{
ctx.proceed();
}
{code}
which is why the state of the interceptor chain is always restored at the end of a
proceed() invocation.
From this, it follows that the problem is not necessarily a thread
safety issue, but a race condition introduced by the asynchronous interceptor
implementation - it is impossible to know whether the next call to ctx.proceed() will
happen before or after the interceptor method returns - and if it happens after, it is
exactly as if the preceding interceptor itself has called proceed(), which requires to
execute the following chain of interceptors again, including the current one.
This works in Open Web Beans because of an actual issue there -
https://issues.apache.org/jira/browse/OWB-570, due to which interceptor executions are not
actually recoverable.
One possible solution is to ignore the requirement of passing the same instance along and
pass a different immutable InvocationContext on each interceptor call. This is, however, a
violation of the specification in itself, so I would like to consider the implications
before moving forward with it.
However, even if we decide how to support this in Weld, the interceptor code itself is
non-portable, since an implementation cannot be required to support this behaviour. An
alternative solution would be to copy the InvocationContext, which will probably need to
be implemented for each CDI implementation in particular.
Interceptors not threadsafe
---------------------------
Key: WELD-862
URL:
https://issues.jboss.org/browse/WELD-862
Project: Weld
Issue Type: Enhancement
Components: Interceptors and Decorators
Affects Versions: 1.1.0.Final
Environment: Jetty, Weld Filter
Reporter: Sebastian Schaffert
Assignee: Marius Bogoevici
I am trying to implement an "@Asynchronous" interceptor that runs methods
annotated with the @Asynchronous annotation in a separate thread. The implementation of
the interceptor currently looks as follows:
private static final ThreadGroup asyncMethods = new ThreadGroup("asynchronous
method invocations");
@AroundInvoke
public Object manageAsynchronous(final InvocationContext ctx) throws Exception {
final UUID threadID = UUID.randomUUID();
Runnable r = new Runnable() {
@Override
public void run() {
try {
log.debug("asynchronous method invocation of {}.{} (Thread ID
{})",new Object[] {ctx.getTarget().getClass().getName(),ctx.getMethod().getName(),
threadID});
Object val = ctx.proceed();
if(val != null) {
log.debug("asynchronous method invocation of {}.{} (Thread
ID {}) returned value {}",new Object[]
{ctx.getClass().getName(),ctx.getMethod().getName(), threadID, val});
}
} catch(Exception ex) {
log.error("exception during asynchronous method
invocation",ex);
}
}
};
Thread t = new Thread(asyncMethods,r);
t.setName("asynchronous method invocation of
"+ctx.getTarget().getClass().getName()+ctx.getMethod().getName() + " (Threaad ID
" + threadID+")");
t.start();
return null;
}
Now the problem is that the interceptor is called infinitely often. The reason is that
the annotated method forks a new thread and then returns instantly, setting the variable
"currentPosition" in SimpleInterceptorChain back to the value 0 (in a
"finally" block). So when the proceed() method is called inside the thread, the
interceptor chain again points to the first interceptor in the chain and it all repeats
infinitely.
--
This message is automatically generated by JIRA.
For more information on JIRA, see:
http://www.atlassian.com/software/jira