Apache Camel 4.x Upgrade Guide

This document is for helping you upgrade your Apache Camel application from Camel 4.x to 4.y. For example, if you are upgrading Camel 4.0 to 4.2, then you should follow the guides from both 4.0 to 4.1 and 4.1 to 4.2.

The Camel Upgrade Recipes project provides automated assistance for some common migration tasks. Note that manual migration is still required. See the documentation page for details.

Upgrading Camel 4.20 to 4.21

camel-grok - potential breaking change

The library used for Camel Grok component has been migrated from no more maintained io.krakens:java-grok to its fork io.github.whatap:java-grok. It implies small differences listed here.

camel-core

The camel-api module now has an optional dependency on org.jspecify:jspecify for null safety annotations (@NullMarked, @Nullable). The dependency is <optional>true</optional>, so it is not pulled transitively into your application. The annotations are used to document nullability contracts across the Camel API surface and enable compile-time null checking with tools like NullAway. If you want to leverage these annotations in your own code or tooling, add org.jspecify:jspecify explicitly to your project dependencies.

The org.apache.camel.support.DefaultHeaderFilterStrategy changed default setting for lowercase from false to true.

The type converters for Java serialized objects with types java.io.ObjectInput and java.io.ObjectOutput has been removed. Java object serialization is a recurring source of security issues and therefore these converters has been removed. These converters are not used at all by Camel itself. To restore compatibility then end users can add these type converters back as custom converters in their own Camel applications. However, using Java serialization is discouraged and highly recommend to use other means.

Removed org.apache.camel.spi.ReifierStrategy which is no longer in use by Camel and can safely be removed.

A new getChildren() default method has been added to org.apache.camel.NamedNode. It returns List<NamedNode> and provides a uniform API for traversing the route model tree, including structural nodes like WhenDefinition and OtherwiseDefinition that are not ProcessorDefinition subclasses and therefore invisible to getOutputs(). Code that walks the model tree recursively should prefer getChildren() over getOutputs() to avoid needing special-case handling for Choice EIP.

The NamedNode.getLevel() and getParentId() methods no longer treat when and otherwise nodes as "shallow". Previously these nodes were flattened to the same level as their parent choice, which was inconsistent with how doCatch and doFinally work inside doTry. Now when and otherwise contribute their own level, consistent with the tree structure exposed by getChildren(). This affects level values reported via JMX (ManagedProcessor.getLevel()) and dev consoles for nodes inside Choice EIP branches.

camel-jbang

The camel wrapper command now installs the scripts as camel instead of camelw. You can use the --command-name=camelw to use the old name.

When camel.main.routesReloadEnabled=true (automatically set by camel run --dev), Camel now auto-disables contentCache on resource-based components (such as xslt) whose default is true, so that edits to the resource file are picked up on the next message without restarting the route. Set camel.component.<name>.contentCache=true (or pass ?contentCache=true on the URI) to opt back in to caching during dev mode.

camel-jbang plugins

Plugins are now loaded lazily. Built-in commands that do not consume plugins (for example camel get, camel version, camel ps, camel stop) skip plugin discovery entirely, avoiding classpath scans and Maven resolution on every invocation. Plugin-consuming commands (run, export, cmd, shell) and plugin-provided commands (such as kubernetes, generate, test) continue to work unchanged. When an external plugin is resolved through Maven, its resolved classpath is cached in ~/.camel-jbang-plugins.json under a new resolved block. Subsequent invocations load the plugin directly from the cached jars without going through the Maven downloader. The cache is validated by file size and modification time on both the cached jars and the plugin’s POM, so SNAPSHOT plugins rebuilt locally are picked up automatically. The cache is also invalidated when the Camel version, the plugin GAV, or the effective --repos/--repo value changes. No user action is required; existing plugin entries are populated on first use after upgrade.

camel-yaml-dsl

A new canonical JSON Schema variant (camelYamlDsl-canonical.json) has been added alongside the existing classic schema (camelYamlDsl.json). The canonical schema removes all implicit patterns (string shorthands, inline expressions, oneOf/anyOf/not constructs) to provide a simpler, more predictable schema for tooling such as IDEs, code generators, and AI assistants. See the YAML DSL documentation for details.

The YamlValidator class now accepts a boolean canonical constructor parameter to validate against the canonical schema.

A new camel yaml normalize command has been added to Camel JBang. It rewrites YAML routes from the classic (shorthand) form to the canonical (explicit) form. The camel validate yaml command also supports a new --canonical flag to validate against the canonical schema.

camel-kafka / Spring Boot

When using camel-kafka-starter with Spring Boot, the standard spring.kafka. properties are now automatically bridged to the Camel Kafka component configuration (CAMEL-22760). This means you no longer need to duplicate Kafka settings under both spring.kafka. and camel.component.kafka.*.

The bridged properties include bootstrap-servers, security.protocol, SSL/TLS settings (keystore, truststore), consumer.group-id, client-id, and SASL properties (sasl.mechanism, sasl.jaas.config, sasl.kerberos.service.name).

Explicit camel.component.kafka.* settings always take precedence over the bridged Spring Boot values.

The bridge is enabled by default. To disable it, set:

camel.component.kafka.bridge-spring-kafka-properties=false

Default deserialization filter tightened

The default ObjectInputFilter pattern that ships with the components listed below has been tightened to explicitly deny classes under java.net. before allowing the rest of java., javax. and org.apache.camel.. The previous default did not deny java.net.**, which meant classes whose hashCode/equals methods perform network I/O (notably java.net.URL and java.net.InetAddress) could be deserialized by the in-code default.

Affected components:

  • camel-jms, camel-sjms, camel-amqp, camel-mina, camel-netty, camel-netty-http, camel-vertx-http, camel-infinispan

  • The aggregation repository components: camel-leveldb, camel-cassandraql, camel-consul, camel-sql (JDBC aggregation repository)

The new default is:

!java.net.**;java.**;javax.**;org.apache.camel.**;!*

(or !java.net.;java.;org.apache.camel.;!* for the aggregation repository components, which do not include javax.).

The endpoint-level option deserializationFilter and the JVM-wide system property -Djdk.serialFilter continue to override this default. Applications that have a legitimate need to deserialize java.net.URL or other java.net.* types must configure an explicit filter.

For production deployments handling untrusted serialized payloads, the in-code filter is intended as defense-in-depth only. The primary mitigation should be configured at the messaging provider:

  • ActiveMQ Artemis: deserializationAllowList / deserializationDenyList (see the Artemis docs)

  • ActiveMQ Classic: the org.apache.activemq.SERIALIZABLE_PACKAGES system property

camel-jms

JMS ObjectMessage support is now disabled by default. Java object serialization is a recurring source of security issues, and Camel JMS routes rarely use ObjectMessage in practice. The component will now refuse to create or read jakarta.jms.ObjectMessage instances unless the new objectMessageEnabled option is explicitly set to true.

This affects the following endpoint/component options that rely on ObjectMessage internally:

  • jmsMessageType=Object (or sending a Serializable body that is auto-detected as Object)

  • transferExchange=true

  • transferException=true

  • receiving a JMS ObjectMessage produced by an external sender

To restore the previous behavior, enable the option at the component or endpoint level:

camel.component.jms.objectMessageEnabled=true

Or, on a single endpoint:

jms:queue:foo?objectMessageEnabled=true

camel-sjms / camel-sjms2

The same default applies to camel-sjms (and camel-sjms2, which inherits from it): JMS ObjectMessage support is now disabled by default and gated by a new objectMessageEnabled option (default false) on SjmsComponent / SjmsEndpoint.

This affects the same endpoint/component options as camel-jms:

  • jmsMessageType=Object (or sending a Serializable body that is auto-detected as Object)

  • transferException=true

  • receiving a JMS ObjectMessage produced by an external sender

To restore the previous behavior, enable the option at the component or endpoint level:

camel.component.sjms.objectMessageEnabled=true
camel.component.sjms2.objectMessageEnabled=true

Or, on a single endpoint:

sjms:queue:foo?objectMessageEnabled=true
sjms2:queue:foo?objectMessageEnabled=true

camel-hazelcast

Hazelcast instances created and managed by Camel (when no user-supplied Config or HazelcastInstance is provided) now apply a default JavaSerializationFilterConfig on the SerializationConfig of the Config built by Camel. The default whitelists the class name prefixes java., javax., org.apache.camel. and blacklists java.net..

This affects:

  • camel-hazelcast component endpoints when neither hazelcastInstance, hazelcastConfigUri, nor a referenced Config is supplied

  • HazelcastAggregationRepository and HazelcastIdempotentRepository when no hazelcastInstance is supplied

  • HazelcastUtil#newInstance() (no-arg)

A user-supplied JavaSerializationFilterConfig (set on the SerializationConfig of a Config provided via hazelcastConfigUri, a referenced Config bean, or already wired into a pre-built HazelcastInstance) is respected and is not overwritten.

Applications that store classes outside the default whitelist on a Hazelcast topic, queue, map, list, set, or in one of the repositories above must provide their own Config with a JavaSerializationFilterConfig configured for their class names.

camel-stomp removal

Camel stomp was deprecated with Camel 4.17. The stomp library didn’t have any activities in the last 10 years. The component is now removed.

camel-aws-xray removal

Camel AWS X-Ray was deprecated with Camel 4.17. Amazon Web Services X-Ray service is in maintenance mode since February 2026. The component is now removed.

camel-guava-eventbus removal

Camel Guava EventBus was deprecated with Camel 4.6. The component is now removed.

camel-grape removal

Camel Grape was deprecated with Camel 4.1. The component is now removed.

camel-elytron removal

Camel Grape was deprecated with Camel 4.0. The component is now removed.

camel-telemetry

In order to prevent some potential span leakage in camel-opentelemetry2 (see notes below) we had to perform some changes and introduce the following parameter:

|`disableCoreProcessors`| false | Disable any inner core processors (any core DSL processor provided in the route, for example `bean`, `log`, ...).

This is required in order to distinguish the older traceProcessors parameter which now accounts exclusively for custom processors. Keeping the default parameter (false) could increase the number of spans generated, though, this is required to guarantee consistency and memory sealing. If you need to reduce the number of spans for any reason you can explicitly provide exclusion configuration.

turn disableCoreProcessors to true and traceProcessors to false to skip entirely all processors. However it is not recommended to disable core processors in order to avoid the generation of third party dependencies orphaned spans.

In this version we have also changed certain interfaces, so, if you have custom telemetry implementations, you may need to adjust to them:

org.apache.camel.telemetry.SpanLifecycleManager

The create method signature has changed:

// Old signature
Span create(String spanName, Span parent, SpanContextPropagationExtractor extractor)

// New signature
Span create(String spanName, String spanKind, Span parent, SpanContextPropagationExtractor
extractor)

org.apache.camel.telemetry.SpanDecorator

A new method must be implemented:

String getSpanKind(String operation)

This method should return the appropriate SpanKind based on the operation. Most implementations can extend from:

  • AbstractSpanDecorator (returns INTERNAL for all operations)

  • AbstractHttpSpanDecorator (returns CLIENT for EVENT_SENT, SERVER for EVENT_RECEIVED)

  • AbstractMessagingSpanDecorator (returns PRODUCER for EVENT_SENT, CONSUMER for EVENT_RECEIVED)

camel-opentelemetry2

In order to prevent a potential leak when running asynchronous components we need to rethink the implementation details of camel-opentelemetry2 and remove the Scope wrapping that, when asynchronous, was opening the Scope in a thread and closing in another (what we had called "dirty" context). We are now removing this wrapping and moving this part exclusively in the custom Camel Processors. Here Camel will take care to open the Opentelemetry scope and close it within the same thread.

What it means is that, from now on, the final user or any third party dependency can only "control" the Opentelemetry context within the boundary of a Processor execution.

This is just an informative note, there is not action expected by the final user.

camel-aws-bedrock

The applyGuardrail producer operation now reads the guardrail identifier from a new dedicated header CamelAwsBedrockGuardrailIdentifier (constant BedrockConstants.GUARDRAIL_IDENTIFIER, typed String) instead of CamelAwsBedrockGuardrailConfig. The CamelAwsBedrockGuardrailConfig header is typed GuardrailConfiguration and is reserved for the converse and converseStream operations; the previous code path silently produced null whenever a route mixed converse and applyGuardrail calls. If you were not setting the guardrail identifier via header, the endpoint-level guardrailIdentifier option continues to work without changes.

camel-jgroups

The Exchange header constants in JGroupsConstants have been renamed to follow the Camel naming convention used across the rest of the component catalog. The Java field names are unchanged; only the header string values have changed:

Constant Previous value New value

JGroupsConstants.HEADER_JGROUPS_CHANNEL_ADDRESS

JGROUPS_CHANNEL_ADDRESS

CamelJGroupsChannelAddress

JGroupsConstants.HEADER_JGROUPS_DEST

JGROUPS_DEST

CamelJGroupsDest

JGroupsConstants.HEADER_JGROUPS_SRC

JGROUPS_SRC

CamelJGroupsSrc

JGroupsConstants.HEADER_JGROUPS_ORIGINAL_MESSAGE

JGROUPS_ORIGINAL_MESSAGE

CamelJGroupsOriginalMessage

Routes that reference the constant symbolically (for example setHeader(JGroupsConstants.HEADER_JGROUPS_DEST, …​)) continue to work without changes. Routes that set the header by its literal string value (for example setHeader("JGROUPS_DEST", …​)) must be updated to use the new value (setHeader("CamelJGroupsDest", …​)).

camel-elasticsearch-rest-client

The Exchange header constants in ElasticSearchRestClientConstant have been renamed to follow the Camel naming convention used across the rest of the component catalog. The Java field names are unchanged; only the header string values have changed:

Constant Previous value New value

ElasticSearchRestClientConstant.ID

ID

CamelElasticsearchId

ElasticSearchRestClientConstant.SEARCH_QUERY

SEARCH_QUERY

CamelElasticsearchSearchQuery

ElasticSearchRestClientConstant.INDEX_SETTINGS

INDEX_SETTINGS

CamelElasticsearchIndexSettings

ElasticSearchRestClientConstant.INDEX_NAME

INDEX_NAME

CamelElasticsearchIndexName

ElasticSearchRestClientConstant.OPERATION

OPERATION

CamelElasticsearchOperation

Routes that reference the constant symbolically (for example setHeader(ElasticSearchRestClientConstant.SEARCH_QUERY, …​)) continue to work without changes. Routes that set the header by its literal string value (for example setHeader("SEARCH_QUERY", …​)) must be updated to use the new value (setHeader("CamelElasticsearchSearchQuery", …​)).

camel-aws2-s3

The listObjects operation now uses the ListObjectsV2 AWS API instead of the deprecated ListObjects API. If you use pojoRequest=true with operation=listObjects, you must change your request and response types:

  • ListObjectsRequestListObjectsV2Request

  • ListObjectsResponseListObjectsV2Response

The non-POJO path (header-based configuration) is updated automatically and requires no changes.

camel-nats

Fixed the JetStream consumer pull subscription mode (which is the default) so that messages are actually consumed from the server. Previously the consumer would set up a pull subscription but never issue any pull/fetch requests, so no messages were delivered until the user explicitly switched to push mode with pullSubscription=false.

Two new endpoint options have been added to control the pull fetch loop:

  • pullBatchSize (default 10) — maximum number of messages to fetch per pull request.

  • pullFetchTimeout (default 1000 ms) — maximum time to wait for a batch on each fetch.

camel-lucene

The Exchange header values exposed by LuceneConstants have been renamed to follow the standard Camel naming convention. The field names are unchanged, so routes referencing the constants (LuceneConstants.HEADER_QUERY, LuceneConstants.HEADER_RETURN_LUCENE_DOCS) continue to work without modification. However, routes that set or read these headers using the raw string values must be updated:

  • QUERYCamelLuceneQuery

  • RETURN_LUCENE_DOCSCamelLuceneReturnLuceneDocs

As a consequence, the generated Endpoint DSL header accessors on LuceneHeaderNameBuilder have been renamed accordingly:

  • qUERY()luceneQuery()

  • returnLuceneDocs()luceneReturnLuceneDocs()

camel-jgroups-raft

The Exchange header constants in JGroupsRaftConstants have been renamed to follow the Camel naming convention used across the rest of the component catalog. The Java field names are unchanged; only the header string values have changed:

Constant Previous value New value

JGroupsRaftConstants.HEADER_JGROUPSRAFT_LOG_SIZE

JGROUPSRAFT_LOG_SIZE

CamelJGroupsRaftLogSize

JGroupsRaftConstants.HEADER_JGROUPSRAFT_COMMIT_INDEX

JGROUPSRAFT_COMMIT_INDEX

CamelJGroupsRaftCommitIndex

JGroupsRaftConstants.HEADER_JGROUPSRAFT_CURRENT_TERM

JGROUPSRAFT_CURRENT_TERM

CamelJGroupsRaftCurrentTerm

JGroupsRaftConstants.HEADER_JGROUPSRAFT_IS_LEADER

JGROUPSRAFT_IS_LEADER

CamelJGroupsRaftIsLeader

JGroupsRaftConstants.HEADER_JGROUPSRAFT_LAST_APPLIED

JGROUPSRAFT_LAST_APPLIED

CamelJGroupsRaftLastApplied

JGroupsRaftConstants.HEADER_JGROUPSRAFT_LEADER_ADDRESS

JGROUPSRAFT_LEADER_ADDRESS

CamelJGroupsRaftLeaderAddress

JGroupsRaftConstants.HEADER_JGROUPSRAFT_RAFT_ID

JGROUPSRAFT_RAFT_ID

CamelJGroupsRaftRaftId

JGroupsRaftConstants.HEADER_JGROUPSRAFT_EVENT_TYPE

JGROUPSRAFT_EVENT_TYPE

CamelJGroupsRaftEventType

JGroupsRaftConstants.HEADER_JGROUPSRAFT_SET_OFFSET

JGROUPSRAFT_SET_OFFSET

CamelJGroupsRaftSetOffset

JGroupsRaftConstants.HEADER_JGROUPSRAFT_SET_LENGTH

JGROUPSRAFT_SET_LENGTH

CamelJGroupsRaftSetLength

JGroupsRaftConstants.HEADER_JGROUPSRAFT_SET_TIMEOUT

JGROUPSRAFT_SET_TIMEOUT

CamelJGroupsRaftSetTimeout

JGroupsRaftConstants.HEADER_JGROUPSRAFT_SET_TIMEUNIT

JGROUPSRAFT_SET_TIMEUNIT

CamelJGroupsRaftSetTimeUnit

Routes that reference the constant symbolically (for example setHeader(JGroupsRaftConstants.HEADER_JGROUPSRAFT_SET_TIMEOUT, …​)) continue to work without changes. Routes that set the header by its literal string value (for example setHeader("JGROUPSRAFT_SET_TIMEOUT", …​)) must be updated to use the new value (setHeader("CamelJGroupsRaftSetTimeout", …​)).

Deprecation of camel-ironmqThe library used had no stable release since 2007. There is no Java library very active for this protocol.

The component camel-ironmq is deprecated. The official library used has been unmaintained since 2017 All the other client libraries (in other languages) are unmaintained since the same amount of time. The whole iron-io GitHub organization has almost no activity.

Deprecation of camel-digitalocean

The component camel-digitalocean is deprecated. The java library used has been unmaintained for several years and there is no replacement.

Deprecation of camel-irc

The component camel-irc is deprecated. The library used had no stable release since 2007. There is no Java library very active for this protocol.

Deprecation of camel-iec-60870

The component camel-iec-60870 is deprecated. The library used to implement it NeoScada is no more maintained since 2021. There are no alternatives in Java with compatible license.