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 3 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)
|
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: five new utility methods
These five methods (hasResponse, getResponse, setResponse, createResponse, createResponseFromInput) 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 differences from getOut():
getResponse()returnsnullwhen no response exists (no lazy creation side-effect)createResponse()creates a new emptyDefaultMessage— for header isolationcreateResponseFromInput()creates a response by copying the IN message — for header propagation
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
7638256 to
77f8d96
Compare
…per methods in core modules Add ExchangeHelper.createResponse() and createResponseFromInput() as idempotent helpers for creating response messages. Migrate all core modules from deprecated Exchange.getOut()/hasOut()/setOut() to the new ExchangeHelper-based API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…per methods in components Migrate all component modules from deprecated Exchange.getOut()/hasOut()/setOut() to ExchangeHelper.hasResponse(), getResponse(), setResponse(), createResponse(), and createResponseFromInput(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uide Update tests to use ExchangeHelper methods instead of deprecated getOut/hasOut/setOut. Add new tests for createResponse() and createResponseFromInput() helpers. Document the new ExchangeHelper response methods in the 4.19 upgrade guide. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77f8d96 to
029f64e
Compare


CAMEL-22549
Summary
Replaces all deprecated
Exchange.getOut(),hasOut(), andsetOut()calls with the modernExchange.getMessage()API across the entire codebase. Introduces five 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)— idempotent: creates a new empty response only if none exists, returns the response messageExchangeHelper.createResponseFromInput(exchange)— idempotent: creates a response by copying the input message only if none exists, returns the response messageWhy 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. It is idempotent: if a response already exists, it returns the existing one without replacing it.createResponseFromInput(): For code that usedgetOut().copyFrom(getIn())to propagate headers from the input,createResponseFromInput()does both steps in one call. It is also idempotent: if a response already exists, it returns it without overwriting.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)createResponseFromInput(exchange)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).Pattern B uses
createResponseFromInput()— used where IN headers should propagate to the response (e.g., bean, enricher, poll enricher, CamelConverter).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.Both
createResponse()andcreateResponseFromInput()are idempotent — safe to call multiple times without replacing an existing response. This eliminates the need for guard clauses likeif (!hasResponse(exchange)) { ... }.Follow-up
A follow-up PR will propose adding
hasResponse(),getResponse(),setResponse(),createResponse(), andcreateResponseFromInput()directly to theExchangeinterface, replacing the deprecatedhasOut()/getOut()/setOut()methods.Changes
Core infrastructure
toProcessor()for Expression and Predicate tocreateResponseFromInput()createResponse()/setResponse()+getMessage()Core processors
getOut().copyFrom(getIn())→createResponseFromInput()hasOut()→hasResponse()prepareOutToIn()Components (production code)
createResponseFromInput()replaces getOut() + manual header propagation), BeanExpression (getResponse() returns null for void methods)instanceofpattern replaceshasOut()+getOut(Class)createResponseFromInput(), skips for DefaultExchangeHolder)createResponse()for header isolation)createResponse())createResponse())Tests
ExchangeHelper.hasResponse/getResponse/setResponse/createResponse/createResponseFromInputDocumentation
camel-4x-upgrade-guide-4_19.adocTest plan
mvn formatter:format impsort:sort