@AroundInvoke
interceptor is called twice on a @WebService
class,
if the intercepted method is called from outside of the application via endpoint as a SOAP web service.
If the very same method is called internally from another bean, it's called only once (as I would expect).
The intercepted method itself is always called only once!
Question 1: Can I make the interceptor to be called only once?
Question 2: If I cannot, is there a transferable (server independent) way to decide in which interceptor I am, so I can ignore the redundant one?
Question 3: Is this behaviour common (and defined and described in some documentation), or is it dependent on my specific environment (JBoss EAP 6.4.0)?
Observation:
InvocationContext
is different for both the calls.contextData
, the InvocationContext
's field for passing data along the interceptor chain, is not an instance of the HashMap
, but WrappedMessageContext
, but it does not wrap the other contextData
anyway.(I removed the package name.)
import javax.jws.WebService;
@WebService
public interface MyEndpoint {
public static final String SERVICE_NAME = "MyEndpointService";
public String getHello();
}
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";
}
}
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();
}
}
Interceptor called
Interceptor called
MyEndpointImpl.getHello() called
To get more runtime information, I added more logging.
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;
}
}
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
I cannot answer your questions directly, but maybe some clarification about the contexts may help you.
The Java EE JAX-WS implementation varies from server to server. For Example Glassfish uses Metro and JBoss uses Apache CXF.
There are different kind of interceptors chains which allow to control programmatically the conditions before and after the request/response processing.
The interceptors for the SOAP web service calls are SOAP handlers and logical handlers (See Oracle documentation). Both can access SOAP message on different levels (the whole or only the payload).
My assumption is that your the interceptor called twice, once for accessing through HTTP/SOAP, and once for access over RMI.
In the first interceptor invocation, what you see as context is org.apache.cxf.jaxws.context.WrappedMessageContext which is a Map implementation. See WarppedMessageContext, Apache CXF web service context. It is invoked for HTTP/SOAP access.
The second invocation is what you expect when using the RMI (probably triggered from Apache CXF, after the SOAP message is processed).
To avoid this you can use third class for logic implementation with interceptor defined. The existing web service implementation class will only delegate to it and will not contain interceptor annotation anymore.
Example code can be seen here: OSCM Project
I had the exact same problem and found a solution.
If you instead of using the @Interceptors style binding use a @InterceptorBinding style binding, then the interceptor is only instantiated and invoked once (at least in my case on WildFly 10.1.0.Final).
This is what your example would look like using @InterceptorBinding style.
Your custom interceptor binding annotation:
import javax.interceptor.InterceptorBinding;
...
@Inherited
@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface MyInterceptorBinding {
Your endpoint:
@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 figured out exactly what the problem is, but I suspect that the @Interceptors style binding is valid for multiple types of interceptors (EJB and CDI) while the @InterceptorBinding style is maybe only valid for CDI interceptors. Maybe a JAX-WS @WebService is both an EJB and a CDI bean?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With