Error Handling in Routes

Error Handling in Routes

Overview

Production integrations need proper error handling -- retry logic, success/failure callbacks, and alerting. This example shows how to combine IM's built-in error handler with route-level error handling patterns.

Built-in Error Handler (Properties-Based)

IM provides a default error handler configured via application.properties. This handles retries automatically for known transient errors:

config/application.properties

# Enable the default error handler (enabled by default)
integration.route-error-handling.enabled=true

# Retry configuration
integration.route-error-handling.maximumRedeliveries=10
integration.route-error-handling.redeliveryDelay=2000
integration.route-error-handling.maximumRedeliveryDelay=300000
integration.route-error-handling.useExponentialBackOff=true
integration.route-error-handling.backOffMultiplier=2.0

# Email notifications on final failure
integration.route-error-handling.enable-sending-mail=true
integration.route-error-handling.email.smtpHost={{smtp.host}}
integration.route-error-handling.email.smtPort={{smtp.port}}
integration.route-error-handling.email.mailFrom=integration_{{pfx.partition}}@pricefx.eu
integration.route-error-handling.email.mailTo={{alert.email}};integration@pricefx.eu
integration.route-error-handling.email.mailSubject={{pfx.partition}} - Integration Error

This handles ConnectException, ConnectTimeoutException, RecoverableException, and HTTP 409/503/504 automatically. All other errors fail immediately after exhausting retries.

Route-Level: onCompletion Pattern

Use onCompletion for success/failure callbacks -- logging, notifications, cleanup:

routes/import-with-error-handling.xml

XML
<routes xmlns="http://camel.apache.org/schema/spring">
    <route id="import-with-error-handling">
        <from uri="file:{{import.products.directory}}?{{archive.file}}&amp;{{read.lock}}"/>
        <log message="Starting product import: ${header.CamelFileNameOnly}" loggingLevel="INFO"/>

        <to uri="pfx-csv:streamingUnmarshal?skipHeaderRecord=true&amp;useReusableParser=true"/>
        <to uri="pfx-api:loaddataFile?objectType=P&amp;mapper=import-products.mapper&amp;batchSize=200000"/>

        <log message="Product import completed: ${header.CamelFileNameOnly}" loggingLevel="INFO"/>

        <!-- Success callback -->
        <onCompletion onCompleteOnly="true">
            <log message="SUCCESS: ${header.CamelFileNameOnly} imported successfully" loggingLevel="INFO"/>
        </onCompletion>

        <!-- Failure callback -->
        <onCompletion onFailureOnly="true">
            <log message="FAILED: ${header.CamelFileNameOnly} - ${exception.message}" loggingLevel="ERROR"/>
        </onCompletion>
    </route>
</routes>

Route-Level: doTry/doCatch Pattern

Use doTry/doCatch when you need to handle specific exceptions differently within the route:

routes/import-with-trycatch.xml

XML
<routes xmlns="http://camel.apache.org/schema/spring">
    <route id="import-with-trycatch">
        <from uri="file:{{import.products.directory}}?{{archive.file}}&amp;{{read.lock}}"/>

        <doTry>
            <to uri="pfx-csv:streamingUnmarshal?skipHeaderRecord=true&amp;useReusableParser=true"/>
            <to uri="pfx-api:loaddataFile?objectType=P&amp;mapper=import-products.mapper&amp;batchSize=200000"/>

            <doCatch>
                <exception>net.pricefx.integration.api.exception.FormatException</exception>
                <log message="Bad CSV format in ${header.CamelFileNameOnly}: ${exception.message}" loggingLevel="ERROR"/>
                <!-- File is already archived by the file consumer -- no need to move it -->
            </doCatch>
            <doCatch>
                <exception>net.pricefx.integration.api.exception.NonRecoverableException</exception>
                <log message="Configuration error: ${exception.message}" loggingLevel="ERROR"/>
            </doCatch>
        </doTry>
    </route>
</routes>

Combined: DMDS Import with Full Error Handling

A production-ready DMDS import combining onCompletion (for mandatory flush) with proper error logging:

routes/import-datasource-production.xml

XML
<routes xmlns="http://camel.apache.org/schema/spring">
    <route id="import-datasource-production">
        <from uri="file:{{import.ds.directory}}?{{archive.file}}&amp;{{read.lock}}"/>
        <log message="Starting DS import: ${header.CamelFileNameOnly}" loggingLevel="INFO"/>

        <split aggregationStrategy="recordsCountAggregation" streaming="true">
            <tokenize token="
" group="10000"/>
            <to uri="pfx-csv:unmarshal?skipHeaderRecord=true"/>
            <to uri="pfx-api:loaddata?objectType=DM&amp;dsUniqueName=Product&amp;mapper=import-datasource-production.mapper"/>
        </split>

        <log message="DS load complete. Records: ${header.PfxTotalInputRecordsCount}, Failed: ${header.PfxTotalFailedInputRecordsCount}" loggingLevel="INFO"/>

        <!-- Flush ONLY on success -- never flush incomplete data -->
        <onCompletion onCompleteOnly="true">
            <log message="Flushing DMDS.Product" loggingLevel="INFO"/>
            <to uri="pfx-api:flush?dataFeedName=DMF.Product&amp;dataSourceName=DMDS.Product"/>
            <log message="Flush complete" loggingLevel="INFO"/>
        </onCompletion>

        <!-- Log failure without flushing -->
        <onCompletion onFailureOnly="true">
            <log message="FAILED: DS import of ${header.CamelFileNameOnly} - data NOT flushed" loggingLevel="ERROR"/>
        </onCompletion>
    </route>
</routes>

Decision Guide

Pattern

When to Use

Properties-based error handler

Always -- provides automatic retry + email for transient errors

onCompletion onCompleteOnly

Post-success actions: flush, notifications, downstream triggers

onCompletion onFailureOnly

Failure logging, alerting, cleanup

doTry/doCatch

Different handling per exception type within the route

Important: onCompletion runs outside the error handler -- it always executes regardless of retry configuration. doTry/doCatch runs inside the route and catches exceptions before they reach the error handler.

Common Pitfalls

  • onCompletion inside <split>: Will execute after every batch, not once at the end. Always place it directly inside <route>, outside <split>.

  • Flushing on failure: Never use onCompletion (without onCompleteOnly) for DMDS flush -- it would flush incomplete data on failure.

  • Catching retryable errors in doCatch: If you catch ConnectException in doCatch, the default error handler will NOT retry it. Let the error handler handle transient errors.

  • Missing email properties: If enable-sending-mail=true but SMTP properties are wrong, the error email itself fails silently. Test email delivery in QA first.

See Also

  • Error Handling Reference -- all properties, retry timeline, email templates

  • Troubleshooting -- common runtime errors and solutions