Croway commented on code in PR #29080:
URL: https://github.com/apache/camel/pull/29080#discussion_r4453750413


##########
components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreProducer.java:
##########
@@ -127,6 +132,22 @@ private long determineTtl(Exchange exchange) {
         return endpoint.getTtl();
     }
 
+    /**
+     * The value to store: the body as it is, except a stream, which can be 
read only once and would be shared by every
+     * exchange that gets the key. A stream is read and stored as its bytes, 
which keep binary content as it is; a
+     * character stream (also a cached one) as its text in the charset of the 
exchange.
+     */
+    private static Object valueToStore(Exchange exchange, Message message) 
throws InvalidPayloadException {
+        Object body = message.getBody();
+        if (body instanceof Reader) {
+            return 
message.getMandatoryBody(String.class).getBytes(ExchangeHelper.getCharset(exchange));
+        }
+        if (body instanceof StreamCache || body instanceof InputStream) {
+            return message.getMandatoryBody(byte[].class);

Review Comment:
   Two small things here:
   
   - The body is converted, but the message keeps the stream, which is now 
consumed. On a `noStreamCaching()` route with a remote backend, 
`backend.get(key)` or `backend.put(...)` may throw a transient error. If the 
error handler then redelivers, the retry reads the consumed `InputStream` and 
stores `byte[0]` with no error. In a test with a backend whose first `get` 
throws and `maximumRedeliveries(1)`, the stored value is `byte[0]` and the 
exchange completes without an exception. Calling `message.setBody(bytes)` 
before the backend calls makes a retry store the same bytes.
   - A `StreamCache` is not reset before it is converted. The `StreamCache` to 
`byte[]` converter uses `writeTo`, and `InputStreamCache.writeTo` writes `buf, 
pos, count - pos`, so a cache that an earlier processor read part of is stored 
cut short. In a test, an `InputStreamCache` of 7 bytes with 2 already read is 
stored as its last 5 bytes. With stream caching on, the channel resets the 
cache before the producer, so all 7 bytes are stored there, but on a 
`noStreamCaching()` route nothing resets it. Calling `cache.reset()` first 
covers that case.
   
   The `direct:putNoStreamCaching` route in the test could cover both cases.



##########
components/camel-state-store/camel-state-store/src/main/java/org/apache/camel/component/statestore/StateStoreProducer.java:
##########
@@ -127,6 +132,22 @@ private long determineTtl(Exchange exchange) {
         return endpoint.getTtl();
     }
 
+    /**
+     * The value to store: the body as it is, except a stream, which can be 
read only once and would be shared by every
+     * exchange that gets the key. A stream is read and stored as its bytes, 
which keep binary content as it is; a
+     * character stream (also a cached one) as its text in the charset of the 
exchange.
+     */
+    private static Object valueToStore(Exchange exchange, Message message) 
throws InvalidPayloadException {
+        Object body = message.getBody();
+        if (body instanceof Reader) {
+            return 
message.getMandatoryBody(String.class).getBytes(ExchangeHelper.getCharset(exchange));

Review Comment:
   The text is encoded in the charset of the exchange that does the `put`, but 
the charset is not stored with the bytes, so a `get` on another exchange cannot 
know it. Two consequences:
   
   - platform-http sets the `CamelCharsetName` exchange property from the 
charset of the request `Content-Type`, for example `ISO-8859-1`. A later get 
route that runs `convertBodyTo(String.class)` decodes as UTF-8, so non-ASCII 
text comes back as replacement characters. In a test, `"café"` put as a 
`StringReader` with `CamelCharsetName=ISO-8859-1` comes back from `get` plus 
`convertBodyTo(String.class)` as `"caf�"`.
   - `ExchangeHelper.getCharset` calls `Charset.forName`. If `CamelCharsetName` 
holds a name it rejects, the `put` of a `Reader` now throws, where before it 
stored the value. For example, with `CamelCharsetName=utf8mb4` it throws 
`UnsupportedCharsetException: utf8mb4`. On main the same `put` succeeds, and a 
`String` body is still stored on the PR head.
   
   A `Reader` already holds decoded text, so always encoding it in UTF-8 would 
avoid both problems. If keeping the charset of the exchange is intended, the 
doc and the upgrade guide should say that the charset is not stored with the 
bytes.



##########
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc:
##########
@@ -4742,6 +4742,15 @@ jsonata read the text as one JSON string, refused bytes 
and failed on a stream.
 the exchange when one is set; otherwise the JSON parser detects the encoding. 
Such text that is not valid JSON now fails
 with the parse error. Other text, a `Map`, a `List` and other objects are used 
as before.
 
+=== camel-state-store - a stream body is stored as its bytes
+
+The `put` and `putIfAbsent` operations of the `state-store` component now read 
a stream body (an `InputStream`, a
+`Reader` or the stream cache of Camel) and store its bytes, so a later `get` 
gives a `byte[]`. A `Reader` is stored as
+its text in the charset of the exchange, UTF-8 by default. Before, the stream 
object itself was stored: the first `get`
+could read it, later ones got an empty stream, and every exchange that got the 
key shared the same stream object. A
+backend that serializes its values could not store such a stream at all. 
Convert the `byte[]` to the type you need,
+for example with `convertBodyTo(String.class)` for text. Other bodies are 
still stored as they are.

Review Comment:
   `convertBodyTo(String.class)` decodes with the charset of the `get` 
exchange, UTF-8 by default, not the charset the bytes were stored in. It is 
worth saying that the charset is not stored, and to use 
`convertBodyTo(String.class, "<charset>")` when the bytes are not UTF-8.



##########
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc:
##########
@@ -4742,6 +4742,15 @@ jsonata read the text as one JSON string, refused bytes 
and failed on a stream.
 the exchange when one is set; otherwise the JSON parser detects the encoding. 
Such text that is not valid JSON now fails
 with the parse error. Other text, a `Map`, a `List` and other objects are used 
as before.
 
+=== camel-state-store - a stream body is stored as its bytes

Review Comment:
   Nit: this section sits between the two camel-jsonata sections ("camel-jolt, 
camel-jsonata - JSON text...", line 4736, and "camel-jsonata - the endpoint 
options of an http resource", line 4754), so it splits them. Could it move 
after the second one?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to