Document Properties
Kbid282L57
Last Modified04-Feb-2020
Added to KB07-Jun-2017
Public AccessEveryone
StatusOnline
Doc TypeGuidelines, Concepts & Cookbooks
Product
  • ICM 7.9
  • ICM 7.10

Concept - Order Creation

1 Introduction

Note

The feature is introduced in 7.9. with a feature flag. Per default the new order creation handler chains are used. To use the old pipeline based approach, add following line to your appserver.properties or development.properties (or some other *.properties file that is loaded).

intershop.order.creationchain.enable = false

1.1 References

2 Glossary

This glossary describes the terms used in this document:

Term

Description

Handler

The implementation of a small functional unit intended to process a single topic which cannot be broken into smaller parts

Handler Definition

The integration of a handler into the context of a chain (using the Component Framework)

ChainA Chain is composed of one or more Handler Definitions to cover logical steps. From the business point of view it is seen as a sub-chain.
Chain DefinitionA complete business flow from business perspective. It comprises one or more sub-chains to one complete "chain".

3 Basic Design Goals

3.1 Application Independence

  • Minimize the application specific handling related to order creation as much as possible, e.g., promotion code handling via sessions.
  • Avoid application specific code in handler implementations, e.g., handlers that access request or session.

3.2 Performance

  • Handlers without any dependence to other handlers should be freely combinable related to their execution order in the handler chain:

    • Example: It might be wise to process fast validation handlers before time-consuming ones in the pre-order creation handler chain.
      However, this can be a bit tricky, because it depends on the particular situation, e.g., checking max item values of three line items might be faster then payment validation checks, but max item check for 1k line items may change conditions completely.

  • Introduce performance sensors for each handler implementation (possibly by an abstract super class). Partners and projects may adjust the execution order of handlers in the pre-order creation handler chain to their needs as a kind of performance fine tuning.

3.3 Reversibility of Actions

  • Handlers that perform changes to persistent data have to provide a reversible method to perform rollbacks if necessary. This behavior avoids complex rollback flows in the case of errors.

4 Basic Handler Chain Framework

4.1 Big Picture

handler_chain_big_picture

4.1.1 Chain Registry

The ChainRegistry contains all ChainDefinition(s) registered for a specific application. A ChainDefinition is added to the ChainRegistry instance of an application either by the Component Framework, if it is specified in an instances.component file, or by adding an instance of ChainDefinition programmatically by calling:

com.intershop.component.handlerchain.capi.ChainRegistry::addChainDefinition(ChainDefinition<?, ?> chainDefinition) : void

A ChainDefinition can be retrieved from the ChainRegistry by providing its Context type, Result type and name:

<C extends Context, R extends Result> com.intershop.component.handlerchain.capi.ChainRegistry::getChainDefinition(Class<C> contextType, Class<R> resultType, String name) : ChainDefinition<C, R>

Example:

...
@Inject ChainRegistry chainRegistry;

ChainDefinition<ExampleContext, ExampleResult> chainDefinition = chainRegistry.getChainDefinition(ExampleContext.class, ExampleResult.class, "ExampleChain");
...

Default Implementation: Class com.intershop.component.handlerchain.internal.ChainRegistryImpl provides a standard implementation of the ChainRegistry interface.

4.1.2 Chain Definition

ChainDefinition consists of one or more Chain(s). If the ChainDefinition contains multiple Chain(s) than these ones are linked together and executed in the order they are added to the ChainDefinition. A Chain is added to the ChainDefinition either by the Component Framework, if it is specified in an instances.component file, or by adding an instance of Chain programmatically by the following method:

com.intershop.component.handlerchain.capi.ChainDefinition::addChain(Chain<C, R> chain) : void

Example:

Following XML content of an instances.component file defines a ChainDefinition with the name ExampleChainDefinition, consisting of the four Chain(s) Chain1, Chain2, Chain3, Chain4, that will be instantiated and added to the ExampleChainDefinition by the Component Framework:

Example Chain Definition (File: instances.component))
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<components xmlns="http://www.intershop.de/component/2010">
    <fulfill requirement="chainDefinition" of="ChainRegistry">
        <instance name="ExampleChainDefinition" with="ChainDefinition" scope="app">
            <fulfill requirement="name" value="ExampleChain" />
            <fulfill requirement="contextType" value="com.mycompany.component.mycomponent.capi.handlerchain.ExampleContext" />
            <fulfill requirement="resultType" value="com.mycompany.component.mycomponent.capi.handlerchain.ExampleResult" />
            <fulfill requirement="resultMergeStrategy" with="com.mycompany.component.mycomponent.capi.handlerchain.ExampleMergeStrategy" />
            <fulfill requirement="chain">
                <instance name="Chain1" with="Chain" scope="app">>
                    <fulfill requirement="name" value="Chain1" />
                    <fulfill requirement="behaviorOnFailure" value="ROLLBACK" />
                    <fulfill requirement="transactional" value="false" />
                    <fulfill requirement="resultFactory" with="com.mycompany.component.mycomponent.capi.handlerchain.ExampleResultFactory" />
                </instance>
                <instance name="Chain2" with="Chain" scope="app">>
                    <fulfill requirement="name" value="Chain2" />
                    <fulfill requirement="behaviorOnFailure" value="ROLLBACK" />
                    <fulfill requirement="transactional" value="true" />
                    <fulfill requirement="resultFactory" with="com.mycompany.component.mycomponent.capi.handlerchain.ExampleResultFactory" />
                </instance>
                <instance name="Chain3" with="Chain" scope="app">>
                    <fulfill requirement="name" value="Chain3" />
                    <fulfill requirement="behaviorOnFailure" value="ROLLBACK" />
                    <fulfill requirement="transactional" value="false" />
                    <fulfill requirement="resultFactory" with="com.mycompany.component.mycomponent.capi.handlerchain.ExampleResultFactory" />
                </instance>
                <instance name="Chain4" with="Chain" scope="app">>
                    <fulfill requirement="name" value="Chain4" />
                    <fulfill requirement="behaviorOnFailure" value="CONTINUE" />
                    <fulfill requirement="transactional" value="false" />
                    <fulfill requirement="resultFactory" with="com.mycompany.component.mycomponent.capi.handlerchain.ExampleResultFactory" />
                </instance>
            </fulfill>
        </instance>
    </fulfill>
</components>

Default Implementation: Class com.intershop.component.handlerchain.internal.ChainDefinitionImpl provides a default implementation of the ChainDefinition interface.

4.1.3 Chain Executor

The ChainExecutor executes a ChainDefinition, by executing all Chains of the ChainDefinition in the order they are added to the ChainDefinition starting from the first one by calling:

<C extends Context, R extends Result> com.intershop.component.handlerchain.capi.execute(ChainDefinition<C, R> chainDefinition, C context) : R

Execution can be started with a specific Chain too, by calling:

<C extends Context, R extends Result> com.intershop.component.handlerchain.capi.ChainExecutor::execute(ChainDefinition<C, R> chainDefinition, String subChain, C context) : R

Execution starts at the Chain with the name provided by the subChain parameter in this case. Execution of previous Chain(s) of subChain is skipped.

Note

If ChainDefinition::BehaviorOnFailure=ROLLBACK and the processing of one Handler of the Chain specified by subChain finishes with an failure then the method:

 

<C extends Context, R extends Result> com.intershop.component.handlerchain.capi.Chain::reverseExecute(C context) : R

is called for all preceding Chain(s) of subChain in the ChainDefinition, even if these Chain(s) have not been executed before.

Example:
// subChain = chain3

chain3.execute()
chain3.reverseExecute()
chain2.reverseExecute()
chain1.reverseExecute()

4.1.4 Chain

A Chain consists of one or more HandlerDefinition(s). A HandlerDefinition is added to a Chain instance either by the Component Framework, if it is specified in an instances.component file, or by adding an instance of HandlerDefinition programmatically by the following method:

Chain::addHandler(HandlerDefinition<C> registration) : void

4.1.4.1 Transactional Behavior

A Chain can span a single transaction over all their Handler(s). This means that all Handler(s) are working in the same transaction.

4.1.4.2 Behavior on Failure

During the execution of the Chain a Handler may indicate an error as result of its processing. The value of the BehaviorOnFailure attribute specified for the Chain determines how the Chain reacts in this case.

4.1.4.2.1 BehaviorOnFailure=STOP

The Chain stops further processing. Subsequent Chain(s) will not be executed.

  • Chain Transaction: Transactional changes of all Handler(s) of this Chain are rolled back.
  • Handler Transaction: Transactional changes of the Handler that indicated the failure are rolled back
4.1.4.2.2 BehaviorOnFailure=ROLLBACK

The Chain stops further processing. Subsequent Chain(s) will not be executed. The predecessor Chain(s) are reverted using the reverse functionality.

  • Chain Transaction: Transactional changes of all Handler(s) of this Chain are rolled back.
  • Handler Transaction: Transactional changes of the Handler that indicated the failure are rolled back.

Method:

com.intershop.component.handlerchain.capi.Handler<C extends Context>::reverseInvoke(C context) : HandlerResult

is called for all Handler(s) of the Chain that have been executed before the one which indicated the failure in reverse order of their previous execution (specified by the position attribute in the HandlerDefinition). Furthermore this method is called for all Handler(s) of previous Chain(s) of the ChainDefinition.

Note that in case of a chain transaction the Handler(s) do not necessarily need to rollback their transactional changes by their own in every case. The complete chain transaction will be rolled back. However, this applies only for the Handler(s) of the Chain that mark changes to the database. Other changes still need to be rolled back by the handler, e.g., the reset of an inventory via an remote inventory service, since such changes are not covered by the database rollback.

4.1.4.2.3 BehaviorOnFailure=CONTINUE

The Chain continues its processing. Subsequent Chain(s) will be executed.

  • Chain Transaction: Transactional changes of all Handler(s) of Chain are rolled back.
  • Handler Transaction: Transactional changes of the Handler that indicated the failure are rolled back.

Default Implementation:  Class com.intershop.component.handlerchain.internal.ChainImpl provides a default implementation of the Chain interface.

Example:

Following XML content of an instances.component file defines three HandlerDefinition(s) instantiated and added to the Chain1 by the Component Framework.

Example Handler Definition (File: instances.component))
<components xmlns="http://www.intershop.de/component/2010">
    <fulfill requirement="handler" of="Chain1">
        <instance with="HandlerDefinition" name="Handler1">
            <fulfill requirement="name" value="Handler1"/>
            <fulfill requirement="position" value="1"/>
            <fulfill requirement="handler">
                <instance with="com.mycompany.component.mycomponent.capi.handler.Handler1"/>
            </fulfill>
        </instance>
    </fulfill>
    <fulfill requirement="handler" of="Chain1">
        <instance with="HandlerDefinition" name="Handler2">
            <fulfill requirement="name" value="Handler2"/>
            <fulfill requirement="position" value="3"/>
            <fulfill requirement="handler">
                <instance with="com.mycompany.component.mycomponent.capi.handler.Handler2"/>
            </fulfill>
        </instance>
    </fulfill>    
    <fulfill requirement="handler" of="Chain1">
        <instance with="HandlerDefinition" name="Handler3">
            <fulfill requirement="name" value="Handler3"/>
            <fulfill requirement="position" value="2"/>
            <fulfill requirement="handler">
                <instance with="com.mycompany.component.mycomponent.capi.handler.Handler3"/>
            </fulfill>
        </instance>
    </fulfill>
</components>

4.1.5 Handler Definition

A HandlerDefinition is a simple structure comprising a Handler implementation, the name of the handler in the Chain and the position of the handler in the execution order of the Chain.

Handler(s) are added to the Chain by HandlerDefinition(s) for the following reasons:

  • Same Handler instance can be added to the same Chain more than once.
  • The position of an handler in the Chain cannot be specified at the Handler instance directly.

4.1.6 Context

Beside the ChainDefinition an instance of the Context interface has to be provided to the ChainExecutor by the caller:

public interface ExampleContext extends Context
{
    ....
}

public class ExampleContextImpl extends ContextImpl implements ExampleContext
{
    ....
}

Sub-types of the Context interface should reflect the specific use-case by defining getter- and setter-methods for the data required by the Handler(s) of the Chain(s), e.g., Handler(s) of a Chain that creates an order from a basket require at least the basket. ChainExecutor forwards the Context to the first Chain of the ChainDefinition that is executed as well as whose successors.

4.1.7 Handler

The Chain itself does not provide any functionality. Functionality is provided by the Handler(s) that are added to the chain.

  • Chain provides the Context as parameter of the invoke(Context context) method to the Handler(s).
  • A Handler may change the Context by providing or changing any data, e.g. an Order.

A Handler returns its processing state that can be:

  • SUCCESS: There were no errors during processing of the handler. Note that there still may be warnings!
  • FAILURE: Processing of the handler led to a failure. Chain decides on further processing based on the value specified for BehaviorOnFailure that can be:
    • STOP: Further processing of the Chain and its successors is stopped. Chain is left.
    • ROLLBACK: The chain is reversed starting with the predecessor of the failing handler. The Handler which failed has to cover the rollback of the successful completed parts of his own actions by itself.
    • CONTINUE: Further processing of the Chain and its successors continues. Handler(s) that are invoked afterwards may be called with another state than expected. So this behavior is suitable for chains that to not change or create any data.
  • STOP : Further processing of the Chain and its successors is stopped. Chain is left.

Processing state of all Handler(s) of the Chain determines the processing state of the Chain:

  • {SUCCESS} -> COMPLETED
  • {STOP}, {SUCCESS, STOP} -> STOPPED
  • {FAILURE}, {SUCCESS, FAILURE} -> STOPPED (BehaviorOnFailure = STOP)
  • {FAILURE}, {SUCCESS, FAILURE} -> COMPLETED (BehaviorOnFailure = CONTINUE)

4.2 Behavior on Error

  • txChain - Transaction of the chain.
  • txHandler - Transaction of the handler.
ScenarioChain
Transaction
Handler
Transaction
Chain
BehaviorOnFailure

Transactional
Behavior

Chain
Behavior
Remark
1aY(Y/N)STOPtxChain.rollbackChain is left.This scenario is not possible with BehaviorOnError.CONTINUE or BehaviorOnError.ROLLBACK.
2aNYSTOPtxHandler.rollbackChain is left.

Invocation of reverseInvoke() method of previous handlers does not make any sense, because all persistent changes are rolled back by the transaction.

2bNNSTOP Chain is left. 
3aYNROLLBACKtxChain.rollbackreverseInvoke() method of all handlers is called before chain is left.Transaction is rolled back as well as reverseInvoke() methods of previous handlers are invoked in this case.
Note that in the case of a re-entry in the chain the changes made by previous handlers have been already committed.
Example: Handlers have already created the order in the order creation chain.
3bNYROLLBACK txHandler.rollbackreverseInvoke() method of all handlers is called before chain is left. 
3cNNROLLBACK  reverseInvoke() method of all handlers is called before chain is left.Nevertheless is invocation of reverseInvoke() method of previous handlers necessary, because there might a previous handler changed persistent data by its own transaction.
4aNYCONTINUEtxHandler.rollbackinvoke() method of next handlers is called. 
4bNNCONTINUE invoke() method of next handlers is called. 

5 Order Creation Handler Chains

For a list of handlers, please have a look at Cookbook - Order Creation | Recipe: List Chains and Handlers.
Name of ChainDescriptionFailure HandlingTransactional
PreOrderCreationChain

This chain is executed before the actual order is created. The purpose of this chain is to check if all preconditions for an actual order are fulfilled. This includes a final basket validation, checks for approval status etc. If all preconditions are fulfilled, the basket is locked to prevent simultaneous checkout of the same basket either via REST or different browsers. The order creation chain might be left in this chain, for example for the approval workflow.

Rollback on Failureno
OrderCreationChain

This chain creates the actual order with all needed entities, like product line items, addresses, payments etc. and also creates the document number for the order.

In case of failures, the chain (and with this the database transaction) is rolled back. The result is the same state as before the order creation was started: with a valid basket and no according order.

Rollback on Failureyes
PaymentAuthorizationOrderCreationChain

This chain takes care of necessary payment authorizations that need a redirect after checkout. If a redirect is necessary, the chain is left for that redirect.

A callback from a payment authorization will jump back into this chain for further order creation handling or more redirects.

If a callback returns with a failure, the order creation is rolled back, which results in a cancelled order.

Rollback on Failureno
PendingPaymentOrderCreationChain

A chain for processing all actions that do not need to wait for a successful completion of the payment, like sending of an order confirmation mail.

Continue on Failureno
PaymentSynchronizationOrderCreationChainThe PaymentSynchronizationChain waits for all asynchronous payment notifications. Notifications from payments start the order creation in this chain. If all notifications were successful, the order creation proceeds with the post order creation handling. If a notification is received with failures, the order creation is rolled back.Rollback on Failureno
PostPaymentOrderCreationChain

This chains contains all post order handling, like creation of gift certificates, all necessary promotion handling, budget handling etc. Problems and warnings in this chain will not lead to a rollback anymore.

Continue on Failureno
The three last chains (PendingPaymentOrderCreationChain, PaymentSynchronizationOrderCreationChain, PostPaymentOrderCreationChain) are needed for the handling of asynchronous payment notifications. If no asynchronous payment is available in the shop, the PaymentSynchronizationOrderCreationChain is not necessary and the PendingPaymentOrderCreationChain could be combined with the PostPaymentOrderCreationChain.

6 Testing

6.1.1 Handler Implementations

Basic handler chain infrastructure and handler implementation classes have to be tested via JUnit mock tests.

6.1.2 Integration Tests

Complete order creation flows - means finally configured (wired) handler chains - have to be tested by JUnit embedded server tests.

Disclaimer

The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.

Customer Support
Knowledge Base
Product Resources
Support Tickets