INT-2460: Remove Message Modification Logic in MS

JIRA: https://jira.spring.io/browse/INT-2460,
https://jira.spring.io/browse/INT-4122

Since the main purpose of the `MessageStore` to persist message for durability and only,
it doesn't make sense to modify `Message` for additional headers like `SAVED` and `CREATED_DATE`.
Such a logic should be a part of metadata stored together with the message.
And it is provided by the out-of-the-box `MessageStore` implementation.
In addition we free ourselves from the reflection operations to retain `ID` and `TIMESTAMP` headers when we add `SAVED` and `CREATED_DATE`

* Control "already saved" logic in the `JdbcMessageStore`s via `DuplicateKeyException` on the `INSERT`.
This is much effective then additional `SELECT` in case of `SAVED` before
* Control "already saved" logic in the  `AbstractConfigurableMongoDbMessageStore` via `DuplicateKeyException` on the `INSERT`.
Since `MongoDbMessageStore` doesn't provide extra `messageId` field, perform extra `SELECT` before store document.
Anyway the `MongoDbMessageStore` isn't recommended for use.
We may consider to deprecate it
* Control "already saved" logic  in the `AbstractKeyValueMessageStore` via `putIfAbsent` operation

With this fix we persist message in the store as is without any modifications when we perform standard serialization procedure.
Any custom serializers should consider to use `MutableMessageBuilder` if there is a requirement to retain `ID` and `TIMESTAMP`

Rework `MongoDbMetadataStore.putIfAbsent()` to normal `findAndModify()` with particular `$setOnInsert`.
Technically the MongoDB query looks like:
```
db.collection.findAndModify({
  query: { _id: $key },
  update: {
    $setOnInsert: { value: $value } // perform modification only on upsert
  },
  new: false,   // don't return new doc if one is upserted
  upsert: true // insert the document if it does not exist
})
```

Move single import to the appropriate JavaDoc

Polishing after rebase
DEBUG messages in `doStoreIfAbsent()` implementations

* To keep track of the extra message information in the `MessageStore`, without `Message` modification, introduce `MessageMetadata` and `MessageHolder`
* Add `MessageStore#getMessageMetadata()`
* Modify MongoDb `MessageStore` to add extra `timestamp` for individual message
* Fix `ConcurrentAggregatorTests` race condition.
Since currently the default release strategy is `SimpleSequenceSizeReleaseStrategy` which is just based on the `MessageGroup` size, there is no guaranty which messages will complete the group in concurrent environment.
The test is really based on the `SequenceAwareMessageGroup` logic to discard the message with the same `correlationId`

Fix `@Copyright` format

Move cast to `MessageHolder` after `Assert.isInstanceOf(MessageHolder.class, messageHolder)`

Retain backward compatibility in the `AbstractKeyValueMessageStore`

Polishing
This commit is contained in:
Artem Bilan
2016-09-29 18:57:16 -04:00
committed by Gary Russell
parent 7c12288d2e
commit a1f554c04d
23 changed files with 436 additions and 293 deletions

View File

@@ -17,7 +17,6 @@
package org.springframework.integration.redis.store;
import java.util.Collection;
import java.util.Set;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.BoundValueOperations;
@@ -72,12 +71,34 @@ public class RedisMessageStore extends AbstractKeyValueMessageStore {
ops.set(objectToStore);
}
catch (SerializationException e) {
throw new IllegalArgumentException("If relying on the default RedisSerializer (JdkSerializationRedisSerializer) " +
"the Object must be Serializable. Either make it Serializable or provide your own implementation of " +
"RedisSerializer via 'setValueSerializer(..)'", e);
rethrowAsIllegalArgumentException(e);
}
}
@Override
protected void doStoreIfAbsent(Object id, Object objectToStore) {
Assert.notNull(id, "'id' must not be null");
Assert.notNull(objectToStore, "'objectToStore' must not be null");
BoundValueOperations<Object, Object> ops = this.redisTemplate.boundValueOps(id);
try {
Boolean present = ops.setIfAbsent(objectToStore);
if (present != null && logger.isDebugEnabled()) {
logger.debug("The message: [" + present + "] is already present in the store. " +
"The [" + objectToStore + "] is ignored.");
}
}
catch (SerializationException e) {
rethrowAsIllegalArgumentException(e);
}
}
private void rethrowAsIllegalArgumentException(SerializationException e) {
throw new IllegalArgumentException("If relying on the default RedisSerializer " +
"(JdkSerializationRedisSerializer) the Object must be Serializable. " +
"Either make it Serializable or provide your own implementation of " +
"RedisSerializer via 'setValueSerializer(..)'", e);
}
@Override
protected Object doRemove(Object id) {
@@ -93,7 +114,6 @@ public class RedisMessageStore extends AbstractKeyValueMessageStore {
@Override
protected Collection<?> doListKeys(String keyPattern) {
Assert.hasText(keyPattern, "'keyPattern' must not be empty");
Set<Object> keys = this.redisTemplate.keys(keyPattern);
return keys;
return this.redisTemplate.keys(keyPattern);
}
}