Split list of Integers in Apache Camel - java

It is as simple as it looks.
How to split List of integers in apache camel without making camel complains when he tries to split the list<Integer> into Bytes instead!
The code looks like:
....
.transform().message(this::doSomeProcessing) // doSomeProcessing returns List<Integer>
.filter(simple("${body.size} != 0"))
.split(body())
.to(someRabbitMQExchange())
....
And here's the exception:
Caused by: java.lang.IllegalArgumentException: Could not convert number [9901400] of type [java.lang.Integer] to target class [java.lang.Byte]: overflow

The issue was in rabbitmq endpoint, it needed the message to be converted to json first.
so, I just added .marshal().json() before .to(someRabbitMQExchange()) and it solved the issue.

Try comment o avoid the filter, run de app and validate again, tell me that happen later of this.

Related

Vert.x - Not able to decode json to list of objects

I'm using the latest version of Vert.x (4.3.2) and I wrote and handler for a route that serves a POST method.
Below my code to convert the JSON body of that method to a List of objects:
Class<List<CartLineItem>> cls = (Class<List<CartLineItem>>)(Object)List.class;
List<CartLineItem> itemsToAdd = rc.body().asPojo(cls);
Unforntunately whenever I try to iterate over itemsToAdd I get the exception: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class com.acme.CartLineItem.
How can I fix this issue without using any other library except Vert.x (I mean .. using Jackson I would provide a TypeReference<List> object)?
In Vert.x 3 there was method that you could use:
Json.decodeValue(result.bodyAsString(), new TypeReference<List<ConditionDTO>>() {});
Since in Vert.x 4 this was removed you will need to use specific class DatabindCodec:
io.vertx.core.json.jackson.DatabindCodec.fromString(result.bodyAsString(), new TypeReference<List<SimpleDTO>>() {})
If you don't want to use external libraries there is an option to iterate over an jsonArray that you have in body assuming body is something like this:
[{"name":"Lazar", "age":27},{"name":"Nikola", "age":33}]
List<SimpleDTO> list = routingContext.body().asJsonArray()
.stream()
.map(json -> Json.decodeValue(json.toString(), SimpleDTO.class))
.collect(Collectors.toList());

Structured logging where logger argument not wanted in message

I'm using structured logging in a Spring Boot app using logstash and sometimes I want to include key values in the log that I don't want to be used in the message of the log. Is there a StructuredArgument or similar that allows for this?
An example of what I am doing currently is something like this:
// import lombok.extern.slf4j.Slf4j;
// import static net.logstash.logback.argument.StructuredArguments.kv;
// import static net.logstash.logback.argument.StructuredArguments.v;
log.info(
"My message with one arg {}",
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
kv("key2", "arg I only want in the json and not in the message"))
Everything works as I intended, by which I mean the log includes both key value pairs and the message only includes the first value in place of the placeholder. The issue is that I get a warning from the compiler which is flagged by intellij (PlaceholderCountMatchesArgumentCount) about the second structured argument and I would like to avoid this without resorting to suppressing/ignoring it
You can use Markers and pass it before your logging message - more details on github.
logger.info(append("key2", "only json"),
"My message with one arg {}",
v("key1":"arg in msg and json"));
I personally don't like this because markers have different purpose, so if structured argument works for you, just ignore warning in IDE.
Anyway, all this json/structured implementations are workarounds for SLF4J 1.*, which has not built for that. There was SLF4J 2.0.0-alpha1 release almost a yeah ago, but it is still in alpha and I haven't used it. But it's API should be ready for key-values that are crusial in nowadays distributed log management systems.
You can make the log message as a constant String, then the code quality checks will not warn this
You can make the structured argument print nothing into the formatted message:
(1) include the second placeholder {} inside the message
(2) use keyValue() instead of kv()
(3) provide the optional messageFormatPattern parameter (JavaDoc) equal to ""
Adjusting your example:
log.info(
"My message with one arg {}{}", //note (1)
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
keyValue("key2", "arg I only want in the json and not in the message", "")) //note (2) + (3)
This will effectively replace the second placeholder with an empty string.

How to validate list body in Javalin

My DELETE request accepts a list of Items that should be deleted.
I want to validate that the request body is a valid list of Item objects.
The example given in the Javalin docs doesn't mention lists.
In order to get the code to compile, I had to do this:
TypedValidator<List> requestValidator = ctx.validatedBodyAsClass(List.class);
List<Item> items = requestValidator.getOrThrow();
logger.info("Received delete request for {}", Arrays.toString(items.toArray()));
logger.info("items is type {}", items.getClass());
for (Item item : items) {
logger.info("Deleting {}", item.name);
}
The validation passes and the ctx body is printed correctly in the following line.
The problem is, there is an unchecked assignment at getOrThrow() and indeed the loop doesn't work:
[qtp1679441380-34] INFO com.myorg.MyClass - Received delete request for [{name=FooName, type=BarType}]
[qtp1679441380-34] INFO com.ericsson.cdzm.ws.controllers.ScheduleController - items is type class java.util.ArrayList
[qtp1679441380-34] WARN io.javalin.core.ExceptionMapper - Uncaught exception
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.myorg.Item
at com.myorg.MyClass.deleteItems(MyClass.java:51)
Edit: The java.util.LinkedHashMap seems to be because actually, items is of type ArrayList<LinkedHashMap<String,String>>. In other words, Javalin didn't parse or validate the body contents at all! It only converted the Json name=value mappings into a Java Map.
What would be a better way to validate the incoming Json and parse it to a list of Items?
I've tested on Javalin 2.6.0 and 2.8.0.
What I had missed, and can't find in the Javalin docs, was that I have to use array types rather than parameterized Collection types. This works:
TypedValidator<Item[]> requestValidator = ctx.bodyValidator(Item[].class);
List<Item> items = Arrays.asList(requestValidator.get());
Still, would love to know why this is - I suspect it is related to Java's type system somehow?
I could also directly access the Jackson ObjectMapper object and use it like you would use Jackson. The drawback is that I don't benefit from Javalin's automatic throwing of BadRequestResponse etc. I think using array types is a small price to pay for this.
try {
List<ScheduleRequest> items = JavalinJackson.getObjectMapper().readValue(ctx.body(), new TypeReference<List<ScheduleRequest>>(){});
} catch (IOException e) {
throw new BadRequestResponse(e.getMessage());
}

How to customize / replace exception messages in Freemarker to make them more meaningful?

I would like to "improve" some exception messages thrown by Freemarker template messages to make the exceptions more meaningful for the users. Although Freemarker has become a lot better in terms of meaningful error messages, there are still cases, where I would like to be more specific.
Example
Freemarker is throwing this exception for a template like this:
<#if (""?number > 1)>foo</#if>
(just an example... imagine the empty string could also be a variable containing an empty string)
value of templateException.getMessage():
(java.lang.String) Can't convert this string to number: ""
The blamed expression:
==> ""?number [in nameless template at line 1, column 7]
----
FTL stack trace ("~" means nesting-related):
- Failed at: #if (""?number > 1) [in nameless template at line 1, column 1]
----
I would like to rephrase this specific case to:
You tried to convert an EMPTY string variable to a number.
I could try my own Exception handler, to contains checks, replace the message and rethrow an Exception like this:
configuration.setTemplateExceptionHandler(new TemplateExceptionHandler() {
public void handleTemplateException(TemplateException te, Environment env, java.io.Writer out)
throws TemplateException {
String message = te.getMessage();
if(StringUtils.contains(message, "Can't convert this string to number: \"\"")){
message = StringUtils.replace(message, "Can't convert this string to number: \"\"", "You tried to convert an EMPTY string variable to a number. Solution: Try checking if the variable is empty to avoid this error.");
}
throw new TemplateException(message, env);
}
});
But this feels very hacky.
My questions:
Is there a way how I can customize the Exception messages Freemarker is throwing? I have the feeling in my TemplateExceptionHandler it is too late, as the message gets constructed much earlier inside Freemarker.
What are common ways to improve / rewrite exception messages from 3rd party libs?
Search and replace may won't work after version updates, as there's no backward compatibility promise regarding the message content.
If the changes you want are generally useful (not only for your project), then you could improve the existing error messages by contributing to FreeMarker (sign Apache CLA, fork on GitHub, make pull request).
The only really correct and flexible way I see is adding l10n support to the error message mechanism, where the message strings aren't hard-wired in to the code (except their defaults), but are retrieved based on message keys from external source. It can be a big work of course, especially as FreeMarker messages are assembled from many smaller pieces.

abstractlist protobuf java

I have an object containing a list sent from a C# client to a Java server. The serialization with protobuf work perfectly and the object is received perfectly in Java. But the class generated with protoc.exe (can we call it a proto class?) have a list that i can't modify. Basically, I have to add some values in it before returning it to C#, but when I try to add a value, i have an exception :
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
...
Here's how i'm adding values:
MyProtoObject.MyResult result = MyProtoObject.MyResut.NewBuilder()
.setId(1)
.setValue(9.135)
.build();
MyObject.getResultList().add(result);
How can i insert values in it?
Maybe it's somewhat of a workaround, but you could try this:
List<MyResult> l = new ArrayList<MyResult>(MyObject.getResultList());
l.add(result);
MyObject.setResultList(l);
Ok after regenerating the proto class, it appears some methods was missing (I probably made mistakes in first generation). So now i can add values in the list :
MyObjectProto.MyObject o = MyObjectProto.MyObject.newBuilder()
.addAllResults(listOfCalculations)
.build();
listOfCalculation is a List of results objects
or just :
MyObjectProto.MyObject o = MyObjectProto.MyObject.newBuilder()
.addResult(calculationResult)
.build();
CalculationResult is a single result object
Thanks to Flavio

Categories