quarkus: Calling method on target bean from interceptor invokes interceptor again; leads to StackOverflowException

We have an aroundInvoke interceptor defined for our beans to do some application-specific permission checks:

@MyPermissionCheck
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class PermissionInterceptor implements Serializable {
	@AroundInvoke
	public Object beforeInvokation(InvocationContext invocationContext) throws Exception {
		BaseBean bean = (BaseBean) invocationContext.getTarget();
		boolean allowed = bean.performCheck(); // Leads to StackOverflowException
		if (allowed) {
			return invocationContext.proceed();
		}
		else {
			throw new RuntimeException("not allowed);
		}
	}
}
@MyPermissionCheck
@Named
@ViewScoped
public class TraceLogSettingsBean extends BaseBean {
}

When this interceptor is called for our bean, it internally, calls a performCheck-method on the bean by getting a bean reference via invocationContext.getTarget(). This bean reference is the Arc proxy and when performCheck is called, it will call the interceptor again and lead to a StackOverflowException.

Expected behavior As per the handling in Weld and other CDI implementations, when the bean method is called from an interceptor, the interceptor should not be called again.

Actual behavior StackOverflowException

Environment (please complete the following information):

  • Quarkus version or git rev: 1.7.0.Final

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 24 (24 by maintainers)

Most upvoted comments

I am going to close this issue because the original problem (self-invocation and interception) is a grey specification area and the current implementation was a design choice - in other words, it is intentional that self-invocation triggers interception.

@leonardowestphal I think your report is unrelated to this issue - please create a separate issue and apart from this code also include a stacktrace so that we can see the error you are getting. The problem seems to be with inheritance rather than self-invocation in your case.

What is the argument against adopting the behavior of Weld and other CDI implementations?

None at all. This needs to be fixed. We were just suggesting temporary workarounds for you in the meantime 😃

@38leinaD the first workaround suggestion by @famod should work as well - the context data can be used to stored anything during the whole interceptor chain. So you could have a flag saying that you already performed bean.performCheck() within this chain and you’d not invoke it again. Something like this:

        @AroundInvoke
        public Object mySuperCoolAroundInvoke(InvocationContext ctx) throws Exception {
            Map<String, Object> contextData = ctx.getContextData();
            if (contextData.containsKey("my_key")) {
                // within this interceptor chain, the method was already called, no need to do it again
                ctx.proceed();
            } else {
                // the check wasn't invoked yet; set a flag and perform it
                contextData.put("my_key", true);
                BaseBean bean = (BaseBean) ctx.getTarget();
                boolean allowed = bean.performCheck(); // Leads to StackOverflowException
                if (allowed) {
                    return ctx.proceed();
                }
                else {
                    throw new RuntimeException("not allowed);
                }
            }
        }