The @AroundInvoke interceptor is called twice in the @WebService class

Summary

@AroundInvoke interceptor is called twice in the @WebService class if the intercepted method is called from outside the application through the endpoint as a SOAP web service.
If the same method is called internally from another bean, it calls only once (as expected).

The most intercepted method is always called only once!

Question 1 . Can I get the interceptor to be called only once?

Question 2 . If I can’t, is there a transfer method (server independent) for the solution in which I intercept, so I can ignore the redundant?

Question 3 : Is this behavior common (and defined and described in some documentation), or does it depend on my specific environment (JBoss EAP 6.4.0)?

Comment:

  • Two calls are not in the same chain of interceptors.
  • This is not the same instance of the interceptor class.
  • The InvocationContext implementation class is different for both calls.
  • It's funny that one of the contextData fields, InvocationContext for transmitting data along the chain of interceptors, is not an HashMap instance, but a WrappedMessageContext , but it does not transfer another contextData in any case.

Minimum reproducible code

(I removed the package name.)

MyEndpoint Interface

 import javax.jws.WebService; @WebService public interface MyEndpoint { public static final String SERVICE_NAME = "MyEndpointService"; public String getHello(); } 

Class MyEndpointImpl

 import javax.interceptor.Interceptors; import javax.jws.WebService; @WebService(endpointInterface = "MyEndpoint", serviceName = MyEndpoint.SERVICE_NAME) @Interceptors({TestInterceptor.class}) public class MyEndpointImpl implements MyEndpoint { @Override public String getHello() { System.out.println("MyEndpointImpl.getHello() called"); return "Hello"; } } 

Class TestInterceptor

 import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; public class TestInterceptor { @AroundInvoke private Object countCalls(InvocationContext ic) throws Exception { System.out.println("Interceptor called"); return ic.proceed(); } } 

Output

 Interceptor called Interceptor called MyEndpointImpl.getHello() called 

More details

To get more runtime information, I added more protocols.

Class MyEndpointImpl

 import java.lang.reflect.Method; import java.util.Map; import javax.interceptor.AroundInvoke; import javax.interceptor.InvocationContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestInterceptor { private static Logger logger = LoggerFactory.getLogger(TestInterceptor.class); private static int callCnt = 0; @AroundInvoke private Object countCalls(InvocationContext ic) throws Exception { final String interceptorClass = this.toString(); final String invocationContextClass = ic.getClass().getName(); final Method method = ic.getMethod(); final String calledClass = method.getDeclaringClass().getName(); final String calledName = method.getName(); final String message = String.format( "%n INTERCEPTOR: %s%n InvocationContext: %s%n %s # %s()", interceptorClass, invocationContextClass, calledClass, calledName); logger.info(message); final int call = ++callCnt; final Map<String, Object> contextData = ic.getContextData(); contextData.put("whoami", call); logger.info("BEFORE PROCEED {}, {}", call, contextData); final Object ret = ic.proceed(); logger.info("AFTER PROCEED {}, {}", call, contextData); return ret; } } 

Output

  INTERCEPTOR: TestInterceptor@74c90b72 InvocationContext: org.jboss.invocation.InterceptorContext$Invocation MyEndpointImpl # getHello() BEFORE PROCEED 1, org.apache.cxf.jaxws. context.WrappedMessageContext@2cfccb1d INTERCEPTOR: TestInterceptor@5226f6d8 InvocationContext: org.jboss.weld.interceptor.proxy.InterceptorInvocationContext MyEndpointImpl # getHello() BEFORE PROCEED 2, {whoami=2} MyEndpointImpl.getHello() called AFTER PROCEED 2, {whoami=2} AFTER PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext@2cfccb1d 
+6
source share
2 answers

I cannot answer your questions directly, but maybe some clarification regarding the contexts may help you.

The implementation of Java EE JAX-WS varies from server to server. For example, Glassfish uses Metro and JBoss uses Apache CXF.

There are various types of interceptor chains that allow you to programmatically control conditions before and after request / response processing.

The interceptors for SOAP web service calls are SOAP handlers and logical handlers (see Oracle documentation ). Both can access the SOAP message at different levels (in general or only in the payload).

My guess is that your interceptor called twice, once for access via HTTP / SOAP and once for access to RMI.

In the first interceptor call, what you see as context is org.apache.cxf.jaxws.context.WrappedMessageContext, which is the map implementation. See WarppedMessageContext , Apache CXF Web Service Context . It is called for HTTP / SOAP access.

The second call is what you expect when using RMI (probably caused by Apache CXF after processing the SOAP message).

To avoid this, you can use the third class for a logical implementation with a specific interceptor. The existing web service implementation class will delegate only to it and will no longer contain an interceptor annotation.

Sample code can be seen here: OSCM Project

+1
source

I had the same problem and found a solution.

If, instead of using the @Interceptors style binding, use the @InterceptorBinding style binding, then the interceptor is created and launched only once (at least in my case on WildFly 10.1.0.Final).

Here's what your example looks like using the @InterceptorBinding style.

Custom annotation for sniffing an interceptor:

 import javax.interceptor.InterceptorBinding; ... @Inherited @InterceptorBinding @Retention(RUNTIME) @Target({METHOD, TYPE}) public @interface MyInterceptorBinding { 

End point:

 @WebService(endpointInterface = "MyEndpoint", serviceName = MyEndpoint.SERVICE_NAME) @MyInterceptorBinding public class MyEndpointImpl implements MyEndpoint { 

Your interceptor:

 import javax.interceptor.Interceptor; import javax.annotation.Priority; ... @Interceptor @MyInterceptorBinding @Priority(Interceptor.Priority.APPLICATION) //we use @Priority to enable this interceptor application-wide, without having to use beans.xml in every module. public class TestInterceptor { @AroundInvoke private Object countCalls(InvocationContext ic) throws Exception { System.out.println("Interceptor called"); return ic.proceed(); } 

I never found out what the problem is, but I suspect that the @Interceptors style binding is valid for several types of interceptors (EJB and CDI), while the @InterceptorBinding style may only apply to CDI interceptors. Maybe JAX-WS @WebService is an EJB and CDI bean?

+1
source

All Articles