Reputation: 922
I've declared a REST endpoint, which calls another route using direct
. In the end of the second route I'm logging the body but it's not the same body returned to the browser.
Here's a small example reproducing the behavior (I'm using Apache Camel with Spring Boot):
@Component
public class EntidadeRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
restConfiguration().bindingMode(json);
rest("/entidade")
.get("/{id}")
.to("direct:search-by-id");
from("direct:search-by-id")
.routeId("search-by-id")
.setProperty("idEntidade", simple("${header.id}"))
.pollEnrich("file:files/mock/dados?noop=true&idempotent=false")
.unmarshal().json(JsonLibrary.Jackson)
.split(jsonpath("$"))
.filter(jsonpath("$[?(@.id == ${property.idEntidade})]"))
.marshal().json(JsonLibrary.Jackson)
.log("${body}");
}
}
I'm calling it from the browser, using the URL: http://localhost:8080/camel/entidade/1 .
On the folder files/mock/dados
I have a single file, called entidades.json
where there's a JSON Array (see below).
I know the split and filter are working because I'm logging the body in that last line of code and this is what appears in the log:
2021-04-28 18:15:15.707 INFO 3905542 --- [nio-8080-exec-1] search-by-id : {"id":"1","tipo":"PF","nome":"João Maria"}
But this is what is returned to the browser (the exact content of the entidades.json
file):
[{"id":"1","tipo":"PF","nome":"João Maria"},{"id":"2","tipo":"PF","nome":"Maria João"},{"id":"3","tipo":"PF","nome":"João Silva"},{"id":"4","tipo":"PF","nome":"José Souza"}]
Why the logged body is not the same that shows in the browser and to fix it?
PS: If I remove those marshal and unmarshal calls, I get the following error in the browser:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.apache.camel.component.file.FileBinding and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.apache.camel.component.file.GenericFile["binding"])
Upvotes: 0
Views: 2298
Reputation: 9418
Without the input-file and knowing the concrete URI you called (including given value for path-variable {id}
), I can only suspect some issues as follows:
You specified the endpoint as GET /entidada/{id}
.
The {id}
is a path-variable.
So let's assume you call GET /entidata/1
with 1 as ID.
Then your JSON file is polled (read), unmarshalled to ... ?
These unmarshal
and marshal
methods are either used to convert between different data-formats or from data-representation to Java-objects (POJOs) if they are internally used (e.g. to pass to a processor Bean etc.).
I suppose the file dados
contains textual data as JSON.
So you can simply read this text into a (textual!) message body (like a text-message, compare JMS etc.) and work with it: (a) split by JSON-path, (b) filter by JSON-path, (c) log this JSON result, (d) send it back as HTTP-response to the calling client (e.g. your browser).
After this you try to split (assuming you have a JSON-array):
// incoming:
// a single enriched message, in body: JSON-array with 4 elements
.split(jsonpath("$")) // what do you expect as output ?
// here the split usually should be ended using `.end` DSL method
.filter(jsonpath("$[?(@.id == ${property.idEntidade})]")) // each array element of JSON body matched against id, e.g. 1
I filled your HTTP-response (JSON array with the 4 people) into online JSON-path evaluator. Evaluation of $
was not a split, but a single element (inside the result array): exactly the original array (with 4 people).
$
denotes simply the root-element.Plus: Usually after the .split()
there follows an .end()
which aggregates them again.
You left that out. I suppose that is an issue, too.
Later you filtered on the REST-given id:
.filter(jsonpath("$[?(@.id == ${property.idEntidade})]"))`
This results in the logged element:
{"id":"1","tipo":"PF","nome":"João Maria"}
The filter worked successfully: just leaving a single one with id 1
.
When adding .log()
to the route chain, this means you are using the Log EIP. In its documentation a warning Tip is given:
Logging message body with streamed messages:
If the message body is stream based, then logging the message body, may cause the message body to be empty afterwards. See this FAQ. For streamed messages you can use Stream caching to allow logging the message body and be able to read the message body afterwards again.
So your empty log message may be caused by a side-effect when using this for logging stream-based message bodies.
Explained in section Difference between log in the DSL and Log component:
The log DSL is much lighter and meant for logging human logs such as
Starting to do …
etc.
Below example (adjusted to your REST route) illustrates its usage:
rest("/entidade")
.get("/{id}")
.log("Processing ${id}")
.to("bean:foo");
I would suggest using the standard Log component by simply using .to()
DSL passing a URI string based on schema log:
together with the required parameter loggerName.
.to("log:filtered-json")
Here the URI-prefix for Log component is log:
. Each message of the stream is logged using loggerName filtered-json
.
Upvotes: 1
Reputation: 922
The error was I need to pass an AggregationStrategy
to the split
. I also need to stop logging the body, because it was consuming the InputStream. After that, I could safely remove those marshal and unmarshal calls.
This is the final code:
@Component
public class EntidadeRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
restConfiguration().bindingMode(json);
rest("/entidade")
.get("/{id}")
.to("direct:search-by-id");
from("direct:search-by-id")
.routeId("search-by-id")
.setProperty("idEntidade", simple("${header.id}"))
.pollEnrich("file:files/mock/dados?noop=true&idempotent=false")
.split(jsonpath("$"), takeFirst(Exchange.FILTER_MATCHED))
.filter(jsonpath("$[?(@.id == ${property.idEntidade})]")).stop()
.end();
}
private AggregationStrategy takeFirst(String matchProperty) {
return ((oldExchange, newExchange) -> {
if (oldExchange == null && newExchange.getProperty(matchProperty, Boolean.class)) {
oldExchange = newExchange;
}
return oldExchange;
});
}
}
Upvotes: 1