Chapter 57. Determining When the Interceptor is Invoked
Chapter 57. Determining When the Interceptor is Invoked Abstract Interceptors are organized into phases. The phase in which an interceptor runs determines what portions of the message data it can access. An interceptor can determine its location in relationship to the other interceptors in the same phase. The interceptor's phase and its location within the phase are set as part of the interceptor's constructor logic. 57.1. Specifying the Interceptor Location When developing a custom interceptor, the first thing to consider is where in the message processing chain the interceptor belongs. The developer can control an interceptor's position in the message processing chain in one of two ways: Specifying the interceptor's phase Specifying constraints on the location of the interceptor within the phase Typically, the code specifying an interceptor's location is placed in the interceptor's constructor. This makes it possible for the runtime to instantiate the interceptor and put in the proper place in the interceptor chain without any explicit action in the application level code. 57.2. Specifying an interceptor's phase Overview Interceptors are organized into phases. An interceptor's phase determines when in the message processing sequence it is called. Developers specify an interceptor's phase its constructor. Phases are specified using constant values provided by the framework. Phase Phases are a logical collection of interceptors. As shown in Figure 57.1, "An interceptor phase" , the interceptors within a phase are called sequentially. Figure 57.1. An interceptor phase The phases are linked together in an ordered list to form an interceptor chain and provide defined logical steps in the message processing procedure. For example, a group of interceptors in the RECEIVE phase of an inbound interceptor chain processes transport level details using the raw message data picked up from the wire. There is, however, no enforcement of what can be done in any of the phases. We recommended that interceptors within a phase adhere to tasks that are in the spirit of the phase. The complete list of phases defined by Apache CXF can be found in Chapter 62, Apache CXF Message Processing Phases . Specifying a phase Apache CXF provides the org.apache.cxf.Phase class to use for specifying a phase. The class is a collection of constants. Each phase defined by Apache CXF has a corresponding constant in the Phase class. For example, the RECEIVE phase is specified by the value Phase.RECEIVE. Setting the phase An interceptor's phase is set in the interceptor's constructor. The AbstractPhaseInterceptor class defines three constructors for instantiating an interceptor: public AbstractPhaseInterceptor(String phase) -sets the phase of the interceptor to the specified phase and automatically sets the interceptor's id to the interceptor's class name. This constructor will satisfy most use cases. public AbstractPhaseInterceptor(String id, String phase) -sets the interceptor's id to the string passed in as the first parameter and the interceptor's phase to the second string. public AbstractPhaseInterceptor(String phase, boolean uniqueId) -specifies if the interceptor should use a unique, system generated id. If the uniqueId parameter is true , the interceptor's id will be calculated by the system. If the uniqueId parameter is false the interceptor's id is set to the interceptor's class name. The recommended way to set a custom interceptor's phase is to pass the phase to the AbstractPhaseInterceptor constructor using the super() method as shown in Example 57.1, "Setting an interceptor's phase" . Example 57.1. Setting an interceptor's phase The StreamInterceptor interceptor shown in Example 57.1, "Setting an interceptor's phase" is placed into the PRE_STREAM phase. 57.3. Constraining an interceptors placement in a phase Overview Placing an interceptor into a phase may not provide fine enough control over its placement to ensure that the interceptor works properly. For example, if an interceptor needed to inspect the SOAP headers of a message using the SAAJ APIs, it would need to run after the interceptor that converts the message into a SAAJ object. There may also be cases where one interceptor consumes a part of the message needed by another interceptor. In these cases, a developer can supply a list of interceptors that must be executed before their interceptor. A developer can also supply a list of interceptors that must be executed after their interceptor. Important The runtime can only honor these lists within the interceptor's phase. If a developer places an interceptor from an earlier phase in the list of interceptors that must execute after the current phase, the runtime will ignore the request. Add to the chain before One issue that arises when developing an interceptor is that the data required by the interceptor is not always present. This can occur when one interceptor in the chain consumes message data required by a later interceptor. Developers can control what a custom interceptor consumes and possibly fix the problem by modifying their interceptors. However, this is not always possible because a number of interceptors are used by Apache CXF and a developer cannot modify them. An alternative solution is to ensure that a custom interceptor is placed before any interceptors that will consume the message data the custom interceptor requires. The easiest way to do that would be to place it in an earlier phase, but that is not always possible. For cases where an interceptor needs to be placed before one or more other interceptors the Apache CXF's AbstractPhaseInterceptor class provides two addBefore() methods. As shown in Example 57.2, "Methods for adding an interceptor before other interceptors" , one takes a single interceptor id and the other takes a collection of interceptor ids. You can make multiple calls to continue adding interceptors to the list. Example 57.2. Methods for adding an interceptor before other interceptors public addBefore String i public addBefore Collection<String> i As shown in Example 57.3, "Specifying a list of interceptors that must run after the current interceptor" , a developer calls the addBefore() method in the constuctor of a custom interceptor. Example 57.3. Specifying a list of interceptors that must run after the current interceptor Most interceptors use their class name for an interceptor id. Add to the chain after Another reason the data required by the interceptor is not present is that the data has not been placed in the message object. For example, an interceptor may want to work with the message data as a SOAP message, but it will not work if it is placed in the chain before the message is turned into a SOAP message. Developers can control what a custom interceptor consumes and possibly fix the problem by modifying their interceptors. However, this is not always possible because a number of interceptors are used by Apache CXF and a developer cannot modify them. An alternative solution is to ensure that a custom interceptor is placed after the interceptor, or interceptors, that generate the message data the custom interceptor requires. The easiest way to do that would be to place it in a later phase, but that is not always possible. The AbstractPhaseInterceptor class provides two addAfter() methods for cases where an interceptor needs to be placed after one or more other interceptors. As shown in Example 57.4, "Methods for adding an interceptor after other interceptors" , one method takes a single interceptor id and the other takes a collection of interceptor ids. You can make multiple calls to continue adding interceptors to the list. Example 57.4. Methods for adding an interceptor after other interceptors public addAfter String i public addAfter Collection<String> i As shown in Example 57.5, "Specifying a list of interceptors that must run before the current interceptor" , a developer calls the addAfter() method in the constuctor of a custom interceptor. Example 57.5. Specifying a list of interceptors that must run before the current interceptor Most interceptors use their class name for an interceptor id.
import org.apache.cxf.message.Message; import org.apache.cxf.phase.AbstractPhaseInterceptor; import org.apache.cxf.phase.Phase; public class StreamInterceptor extends AbstractPhaseInterceptor<Message> { public StreamInterceptor() { super(Phase.PRE_STREAM); } } public class MyPhasedOutInterceptor extends AbstractPhaseInterceptor { public MyPhasedOutInterceptor() { super(Phase.PRE_LOGICAL); addBefore(HolderOutInterceptor.class.getName()); } } public class MyPhasedOutInterceptor extends AbstractPhaseInterceptor { public MyPhasedOutInterceptor() { super(Phase.PRE_LOGICAL); addAfter(StartingOutInterceptor.class.getName()); } }