CAMEL-22549: Replace deprecated Exchange getOut/hasOut/setOut with getMessage API#22358
CAMEL-22549: Replace deprecated Exchange getOut/hasOut/setOut with getMessage API#22358gnodet wants to merge 4 commits intoapache:mainfrom
Conversation
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
gnodet
left a comment
There was a problem hiding this comment.
Claude Code on behalf of Guillaume Nodet
Self-review: inline comments explaining why each change pattern is valid.
|
gnodet
left a comment
There was a problem hiding this comment.
Claude Code on behalf of Guillaume Nodet
Self-review of the getOut() → getMessage() migration. The PR introduces three new ExchangeHelper utility methods (hasResponse, getResponse, setResponse) that wrap the deprecated Exchange.hasOut(), getOut(), setOut() calls into a single deprecated call site. The changes fall into several patterns explained inline below.
Migration patterns used:
| Pattern | Old code | New code | Behavioral change? |
|---|---|---|---|
| A | getOut().setBody(x) |
getMessage().setBody(x) |
No — getMessage() returns OUT if it exists, IN otherwise |
| B | getOut().copyFrom(in) |
setResponse(in.copy()) |
No — equivalent, creates OUT with IN content |
| C | hasOut() ? getOut() : getIn() |
getMessage() |
No — exact same semantics |
| D | getOut().setBody(x) (lazy creation) |
setResponse(new DefaultMessage()) + getMessage().setBody(x) |
No — explicit creation replaces lazy creation |
| E | hasOut() → read getOut() |
getResponse() (returns null if no OUT) |
Yes — no lazy creation side effect |
| F | setOut(null) |
setResponse(null) |
No — mechanical rename |
| result = resultExchange.hasOut() ? resultExchange.getOut().getBody() : null; | ||
| // the bean component creates an OUT for non-void methods on OUT-capable exchanges, | ||
| // so getResponse() returns null for void methods (no result) and the OUT for non-void | ||
| Message response = ExchangeHelper.getResponse(resultExchange); |
There was a problem hiding this comment.
Pattern E: getResponse() returns null for void methods
The bean component only creates an OUT for non-void methods on OUT-capable exchanges. getResponse() returns null when no OUT exists (void method case), unlike getOut() which would have lazily created an empty one. The null check below handles this correctly.
|
|
||
| Message answer = exchange.getOut(); | ||
| // separate response from request so response headers don't mix with request headers | ||
| ExchangeHelper.setResponse(exchange, new DefaultMessage(exchange.getContext())); |
There was a problem hiding this comment.
Pattern D: explicit new DefaultMessage() to isolate response from request
Uses an empty message (not in.copy()) so that HTTP response headers don't mix with request headers. This preserves the old getOut() behavior which also created an empty message.
| if (payload.outBody != null) { | ||
| exchange.getOut().setBody(payload.outBody); | ||
| // reuse existing response message to preserve its type (e.g. JmsMessage) | ||
| if (!ExchangeHelper.hasResponse(exchange)) { |
There was a problem hiding this comment.
Reuse existing response message to preserve its type
When unmarshalling a transferExchange payload, the OUT message may already be a specialized type (e.g., JmsMessage). The old code unconditionally replaced it with a DefaultMessage, losing the type. Now we only create a new one if none exists.
| NettyPayloadHelper.setOut(exchange, body); | ||
| return exchange.getOut(); | ||
| // DefaultExchangeHolder unmarshals its own OUT, so only pre-create one for normal payloads | ||
| if (!(body instanceof DefaultExchangeHolder)) { |
There was a problem hiding this comment.
Pattern D with guard: skip pre-creation for DefaultExchangeHolder
When transferExchange=true, the body is a DefaultExchangeHolder whose unmarshal() sets up its own OUT message. Pre-creating one here would be overwritten. For normal payloads, we create OUT from in.copy() so setPayload() populates the response via getMessage().
| transformer.setParameter("in", exchange.getIn()); | ||
| transformer.setParameter("out", exchange.getOut()); | ||
| // only set "out" param if a response exists; avoids creating an empty OUT as side effect | ||
| Message response = ExchangeHelper.getResponse(exchange); |
There was a problem hiding this comment.
Pattern E: conditional parameter avoids side-effect OUT creation
The old code always called exchange.getOut() which lazily created an empty OUT as a side effect. Now we only set the "out" transformer parameter if a response actually exists.
| // so that it can mutate it if necessary | ||
| Message out = exchange.getOut(); | ||
| out.copyFrom(in); | ||
| ExchangeHelper.setResponse(exchange, in.copy()); |
There was a problem hiding this comment.
Pattern B: setResponse(in.copy()) replaces getOut().copyFrom(in)
Mechanically equivalent. The same pattern is used in UnmarshalProcessor, Enricher.prepareResult(), PollEnricher.prepareResult(), CamelConverter, and ExchangeHelper.setInOutBodyPatternAware().
| protected void setMessageId(Exchange exchange) { | ||
| if (exchange.hasOut()) { | ||
| JmsMessage out = exchange.getOut(JmsMessage.class); | ||
| if (ExchangeHelper.getResponse(exchange) instanceof JmsMessage out) { |
There was a problem hiding this comment.
Pattern E: instanceof replaces hasOut() + getOut(Class)
getResponse() returns null when no OUT exists, and instanceof naturally handles null (evaluates to false). This is more concise than the old hasOut() guard + cast. Same pattern in SjmsProducer.setMessageId().
| } | ||
| node.addMixin("mix:referenceable"); | ||
| exchange.getOut().setBody(node.getIdentifier()); | ||
| ExchangeHelper.setResponse(exchange, new DefaultMessage(exchange.getContext())); |
There was a problem hiding this comment.
Pattern D: explicit response creation replaces getOut().setBody()
The old getOut().setBody(id) lazily created an empty OUT and set the body. Now we explicitly create the response. Uses new DefaultMessage() (not in.copy()) because the response body (node UUID) is unrelated to the request body. Same pattern in CxfRsInvoker.
| if (exchange.get() != null && exchange.get().hasOut()) { | ||
| return exchange.get().getOut().getBody(); | ||
| if (exchange.get() != null) { | ||
| Message response = ExchangeHelper.getResponse(exchange.get()); |
There was a problem hiding this comment.
Pattern E + deprecated: out:body() / out:header() now null-safe
The old getOut().getBody() would lazily create an empty OUT (side effect). Now getResponse() returns null when no OUT exists, and we return null instead. The getter/setter methods for these functions are also marked @Deprecated.
|
🧪 CI tested the following changed modules:
Build reactor — dependencies compiled but only changed modules were tested (64 modules)
|
…odules Introduce hasResponse(), getResponse(), setResponse() in ExchangeHelper as non-deprecated replacements for Exchange.hasOut()/getOut()/setOut(). Migrate all core modules: ExchangeHelper internals, CamelConverter, MarshalProcessor, UnmarshalProcessor, DefaultExchangeHolder, Enricher, PollEnricher, TransformProcessor, WireTapProcessor, OnCompletionProcessor, and RedeliveryErrorHandler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…essage API Replace deprecated Exchange.getOut(), hasOut(), setOut() with ExchangeHelper.hasResponse(), getResponse(), setResponse() and Exchange.getMessage() across all affected components including: bean, cxf, http, jms, sjms, netty, mina, xpath, xslt, file, jcr, salesforce, xmlsecurity, and 30+ others. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uide Add 7 unit tests for ExchangeHelper.hasResponse/getResponse/setResponse. Update 18 test files to remove incidental deprecated API usage. Add upgrade guide entry in camel-4x-upgrade-guide-4_19.adoc documenting the new ExchangeHelper utilities for component developers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d697782 to
b6889f7
Compare
| public static boolean hasResponse(Exchange exchange) { | ||
| return exchange.getIn() != exchange.getMessage(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the response message if one has been explicitly set, null otherwise. Unlike the deprecated | ||
| * {@link Exchange#getOut()}, this does NOT lazily create an empty message. | ||
| * <p> | ||
| * This is provided as a utility until a proper {@code getResponse()} method is added to the {@link Exchange} API. | ||
| */ | ||
| public static Message getResponse(Exchange exchange) { | ||
| return hasResponse(exchange) ? exchange.getMessage() : null; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the response message on this exchange. Unlike {@link Exchange#getOut()} which lazily creates an empty | ||
| * message on read, this makes response creation explicit. | ||
| * <p> | ||
| * This is a non-deprecated equivalent of {@link Exchange#setOut(Message)}, provided as a utility until a proper | ||
| * {@code setResponse(Message)} method is added to the {@link Exchange} API. | ||
| */ | ||
| @SuppressWarnings("deprecation") | ||
| public static void setResponse(Exchange exchange, Message response) { | ||
| exchange.setOut(response); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new empty response message on the exchange and returns it. This is the non-deprecated equivalent of the | ||
| * lazy-creation side effect of {@link Exchange#getOut()}. | ||
| * <p> | ||
| * Typical usage: | ||
| * | ||
| * <pre> | ||
| * Message response = ExchangeHelper.createResponse(exchange); | ||
| * response.setBody(result); | ||
| * </pre> | ||
| */ | ||
| public static Message createResponse(Exchange exchange) { | ||
| setResponse(exchange, new DefaultMessage(exchange.getContext())); | ||
| return exchange.getMessage(); | ||
| } |
There was a problem hiding this comment.
Foundation: four new utility methods
These four methods (hasResponse, getResponse, setResponse, createResponse) are the non-deprecated equivalents of hasOut(), getOut(), setOut(). They live in ExchangeHelper as a stepping stone — the plan is to eventually promote them to the Exchange API itself.
Key semantic difference from getOut(): getResponse() returns null when no response exists (no lazy creation side-effect). For code that needs the old lazy-creation behavior, use createResponse().
Claude Code on behalf of Guillaume Nodet
| private void fillResult(Exchange exchange, Object result) { | ||
| LOG.trace("Setting bean invocation result : {}", result); | ||
|
|
||
| // the bean component forces OUT if the MEP is OUT capable | ||
| boolean out = exchange.hasOut() || ExchangeHelper.isOutCapable(exchange); | ||
| Message old; | ||
| if (out) { | ||
| old = exchange.getOut(); | ||
| // propagate headers | ||
| exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders()); | ||
| } else { | ||
| old = exchange.getIn(); | ||
| // the bean component forces OUT if the MEP is OUT capable; | ||
| // in.copy() both creates the OUT and propagates headers in one step | ||
| if (ExchangeHelper.isOutCapable(exchange) && !ExchangeHelper.hasResponse(exchange)) { | ||
| ExchangeHelper.setResponse(exchange, exchange.getIn().copy()); | ||
| } | ||
| Message old = exchange.getMessage(); |
There was a problem hiding this comment.
Pattern B: in.copy() replaces getOut() + manual header propagation
The old code did:
boolean out = exchange.hasOut() || ExchangeHelper.isOutCapable(exchange);
if (out) {
old = exchange.getOut(); // lazily creates empty OUT
exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders()); // manual propagation
} else {
old = exchange.getIn();
}Now in.copy() both creates the OUT and propagates headers in one step. The guard !hasResponse(exchange) avoids overwriting if an OUT already exists from an earlier processor in the chain.
Claude Code on behalf of Guillaume Nodet
| public static Message getResultMessage(Exchange exchange) { | ||
| if (exchange.getPattern().isOutCapable()) { | ||
| return exchange.getOut(); | ||
| // explicitly create response if none exists (preserves old getOut() lazy-creation semantics) | ||
| if (!hasResponse(exchange)) { | ||
| return createResponse(exchange); | ||
| } | ||
| return exchange.getMessage(); |
There was a problem hiding this comment.
Explicit response creation preserves getOut() lazy-creation semantics
getResultMessage() is used by CXF, SOAP, and other components that expect an OUT message to exist after calling this method. The old code returned exchange.getOut() which lazily created an empty OUT as a side effect. The new code makes this explicit via createResponse() — the intent is now visible in the code rather than hidden behind a getter.
Claude Code on behalf of Guillaume Nodet
5fc83d2 to
468ad7f
Compare
Introduce createResponse(exchange) that creates a new empty response message and returns it, simplifying the common two-step pattern of setResponse() + getMessage(). Refactored 7 call sites across core and components to use the new utility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
468ad7f to
ea607bc
Compare


CAMEL-22549
Summary
Replaces all deprecated
Exchange.getOut(),hasOut(), andsetOut()calls with the modernExchange.getMessage()API across the entire codebase. Introduces four newExchangeHelperbridge methods to centralize the migration:ExchangeHelper.hasResponse(exchange)— non-deprecated equivalent ofhasOut()ExchangeHelper.getResponse(exchange)— returns the response message without lazy creation (unlikegetOut())ExchangeHelper.setResponse(exchange, message)— wrapssetOut()as the single remaining call siteExchangeHelper.createResponse(exchange)— creates a new empty response and returns it (replacesgetOut()lazy-creation pattern)Why new utility methods?
The new methods are not just renames of the deprecated API — they address the specific problems that led to the deprecation:
getResponse()≠getOut():getOut()lazily creates a new empty OUT message as a side effect if none exists — a "getter" that silently mutates state.getResponse()returnsnullif there's no response, with no side effects.createResponse(): For code that relied ongetOut()lazy creation,createResponse()makes the intent explicit — it creates a new emptyDefaultMessage, sets it as the response, and returns it.hasResponse()avoids deprecated API: Functionally equivalent tohasOut(), but implemented asgetIn() != getMessage()— same semantics without calling the deprecated method.setResponse()centralizes the deprecated call: Instead of 50+ scatteredsetOut()calls, there is now exactly one — insidesetResponse(). When the deprecated API is eventually removed, only this single method needs to change.Migration patterns
getOut().setBody(x)getMessage().setBody(x)getOut().copyFrom(in)setResponse(in.copy())hasOut() ? getOut() : getIn()getMessage()getOut().setBody(x)(lazy creation)createResponse(exchange).setBody(x)hasOut()→ readgetOut()getResponse()(returns null if no OUT)setOut(null)setResponse(null)Pattern D uses
createResponse()which creates anew DefaultMessage()— used where code relied ongetOut()creating an empty OUT message (e.g.,HttpProducer,JcrProducer,UnmarshalProcessor). Where IN headers should propagate to the response (e.g., bean, enricher),setResponse(exchange.getIn().copy())is used instead.Pattern E is the only behavioral change:
getResponse()returns null instead of lazily creating an empty message. Code using this pattern now has explicit null checks.Follow-up
A follow-up PR will propose adding
hasResponse(),getResponse(),setResponse(), andcreateResponse()directly to theExchangeinterface, replacing the deprecatedhasOut()/getOut()/setOut()methods.Changes
Core infrastructure
toProcessor()for Expression and PredicatecreateResponse()/setResponse()+getMessage()Core processors
getOut().copyFrom(getIn())→setResponse(getIn().copy())hasOut()→hasResponse()prepareOutToIn()Components (production code)
in.copy()replaces getOut() + manual header propagation), BeanExpression (getResponse() returns null for void methods)instanceofpattern replaceshasOut()+getOut(Class)createResponse()for header isolation)createResponse())createResponse())Tests
ExchangeHelper.hasResponse/getResponse/setResponse/createResponseDocumentation
camel-4x-upgrade-guide-4_19.adocTest plan
mvn formatter:format impsort:sort