Edit on GitHub << back to Interceptors

# Chaining Interceptor

## Description

An interceptor that copies all the properties of every object in the value stack to the currently executing object, except for any object that implements Unchainable. A collection of optional `includes` and `excludes` may be provided to control how and which parameters are copied. Only `includes` or `excludes` may be specified. Specifying both results in undefined behavior. See the JavaDocs for ReflectionProvider#copy(Object, Object, java.util.Map, java.util.Collection, java.util.Collection) for more information.

> It is important to remember that this interceptor does nothing if there are no objects already on the stack. This means two things:

> 1. you can safely apply it to all your actions without any worry of adverse affects.
> 2. it is up to you to ensure an object exists in the stack prior to invoking this action. The most typical way this is done is through the use of the chain result type, which combines with this interceptor to make up the action chaining feature.

By default Errors, Field errors and Message aren’t copied during chaining, to change the behaviour you can specify the below three constants in struts.properties or struts.xml:

- `struts.xwork.chaining.copyErrors` - set to true to copy Action Errors
- `struts.xwork.chaining.copyFieldErrors` - set to true to copy Field Errors
- `struts.xwork.chaining.copyMessages` - set to true to copy Action Messages

### Example

    <constant name="struts.xwork.chaining.copyErrors" value="true"/>

## Parameter Authorization

By default the Chaining Interceptor copies all properties of the objects on the value stack into the target action, regardless of any `@StrutsParameter` annotation. To restrict copying to annotated properties only, set the global constant:

    <constant name="struts.chaining.requireAnnotations" value="true"/>

When enabled (default is `false`):

- Only properties whose target setters carry `@StrutsParameter` are copied; rejected properties are skipped and logged at `WARN`.
- Authorization uses the same `ParameterAuthorizer` service as the Parameters Interceptor, keeping semantics consistent.
- The behaviour is fail-closed: if the target action cannot be introspected, no properties are copied.
- This is a global constant only — there is no per-interceptor override.

## Security Considerations

Action chaining makes the data path into the target action implicit: the target is populated from whatever happens to be on the value stack rather than from an explicit call. Keep that in mind when designing chained actions:

- Do not carry authorization, trust, identity, or approval state across a chain. Re-derive such state from the session or your security context inside the target action, so it never depends on what a previous action left on the stack.
- Prefer avoiding chaining where practical. It couples the two actions together and makes the target’s inputs harder to reason about; a shared service or an explicit redirect is usually clearer.
- If chaining is required, narrow what is copied. Implement Unchainable on objects that must never be copied from, and use the interceptor’s `includes` or `excludes` parameters to limit the copied properties to the ones the target genuinely needs.
- Do not expose a public setter on the target action for state that must not be settable from outside that action. Anything with a public setter is, by design, part of the action’s input surface.

See also Where authorization applies for an overview of the channels that can populate an action.

## Parameters

- `excludes` (optional) - the list of parameter names to exclude from copying (all others will be included)
- `includes` (optional) - the list of parameter names to include when copying (all others will be excluded)

## Extending the Interceptor

There are no known extension points to this interceptor.

## Examples

Simple example how to chain two actions

    <action name="someAction" class="com.examples.SomeAction">
        <interceptor-ref name="basicStack"/>
    	<result name="success" type="chain">otherAction</result>
    </action>

    <action name="otherAction" class="com.examples.OtherAction">
        <interceptor-ref name="chain"/>
    	<interceptor-ref name="basicStack"/>
    	<result name="success">good_result.ftl</result>
    </action>

This examples chains two actions but only one property from first action is copied to another

    <action name="someAction" class="com.examples.SomeAction">
        <interceptor-ref name="basicStack"/>
    	<result name="success" type="chain">otherAction</result>
    </action>

    <action name="otherAction" class="com.examples.OtherAction">
        <interceptor-ref name="chainStack">
    		<param name="chain.includes">prop1</param>
    	</interceptor-ref>
    	<result name="success">good_result.ftl</result>
    </action>

Follow @x