use of org.springframework.integration.store.SimpleMessageStore in project spring-integration by spring-projects.
the class AggregatorAnnotationPostProcessor method createHandler.
@Override
protected MessageHandler createHandler(Object bean, Method method, List<Annotation> annotations) {
MethodInvokingMessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(bean, method);
processor.setBeanFactory(this.beanFactory);
MethodInvokingReleaseStrategy releaseStrategy = null;
Method releaseStrategyMethod = MessagingAnnotationUtils.findAnnotatedMethod(bean, ReleaseStrategy.class);
if (releaseStrategyMethod != null) {
releaseStrategy = new MethodInvokingReleaseStrategy(bean, releaseStrategyMethod);
}
MethodInvokingCorrelationStrategy correlationStrategy = null;
Method correlationStrategyMethod = MessagingAnnotationUtils.findAnnotatedMethod(bean, CorrelationStrategy.class);
if (correlationStrategyMethod != null) {
correlationStrategy = new MethodInvokingCorrelationStrategy(bean, correlationStrategyMethod);
}
AggregatingMessageHandler handler = new AggregatingMessageHandler(processor, new SimpleMessageStore(), correlationStrategy, releaseStrategy);
String discardChannelName = MessagingAnnotationUtils.resolveAttribute(annotations, "discardChannel", String.class);
if (StringUtils.hasText(discardChannelName)) {
handler.setDiscardChannelName(discardChannelName);
}
String outputChannelName = MessagingAnnotationUtils.resolveAttribute(annotations, "outputChannel", String.class);
if (StringUtils.hasText(outputChannelName)) {
handler.setOutputChannelName(outputChannelName);
}
String sendPartialResultsOnExpiry = MessagingAnnotationUtils.resolveAttribute(annotations, "sendPartialResultsOnExpiry", String.class);
if (sendPartialResultsOnExpiry != null) {
handler.setSendPartialResultOnExpiry(Boolean.parseBoolean(this.beanFactory.resolveEmbeddedValue(sendPartialResultsOnExpiry)));
}
handler.setBeanFactory(this.beanFactory);
return handler;
}
use of org.springframework.integration.store.SimpleMessageStore in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testReaperDoesntReapAProcessingGroup.
// INT-2751
@Test
public void testReaperDoesntReapAProcessingGroup() throws Exception {
final MessageGroupStore groupStore = new SimpleMessageStore();
final CountDownLatch waitForSendLatch = new CountDownLatch(1);
final CountDownLatch waitReapStartLatch = new CountDownLatch(1);
final CountDownLatch waitReapCompleteLatch = new CountDownLatch(1);
AbstractCorrelatingMessageHandler handler = new AbstractCorrelatingMessageHandler(group -> group, groupStore) {
@Override
protected void afterRelease(MessageGroup group, Collection<Message<?>> completedMessages) {
}
};
handler.setReleasePartialSequences(true);
/*
* Runs "reap" when group 'bar' is in completion
*/
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(() -> {
try {
waitReapStartLatch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
waitForSendLatch.countDown();
try {
Thread.sleep(100);
} catch (InterruptedException e2) {
Thread.currentThread().interrupt();
}
groupStore.expireMessageGroups(50);
waitReapCompleteLatch.countDown();
});
final List<Message<?>> outputMessages = new ArrayList<Message<?>>();
handler.setOutputChannel((message, timeout) -> {
/*
* Executes when group 'bar' completes normally
*/
outputMessages.add(message);
// wake reaper
waitReapStartLatch.countDown();
try {
waitForSendLatch.await(10, TimeUnit.SECONDS);
// wait a little longer for reaper to grab groups
Thread.sleep(2000);
// simulate tx commit
groupStore.removeMessageGroup("bar");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return true;
});
handler.setReleaseStrategy(group -> group.size() == 2);
QueueChannel discards = new QueueChannel();
handler.setDiscardChannel(discards);
handler.setSendPartialResultOnExpiry(true);
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId("qux").build();
// partial group that will be reaped
handler.handleMessage(message);
message = MessageBuilder.withPayload("foo").setCorrelationId("bar").build();
// full group that should not be reaped
handler.handleMessage(message);
message = MessageBuilder.withPayload("baz").setCorrelationId("bar").build();
handler.handleMessage(message);
assertTrue(waitReapCompleteLatch.await(10, TimeUnit.SECONDS));
// Before INT-2751 we got bar + bar + qux
// bar + qux
assertEquals(2, outputMessages.size());
// normal release
// 'bar'
assertEquals(2, ((MessageGroup) outputMessages.get(0).getPayload()).size());
// reaper release
// 'qux'
assertEquals(1, ((MessageGroup) outputMessages.get(1).getPayload()).size());
assertNull(discards.receive(0));
exec.shutdownNow();
}
use of org.springframework.integration.store.SimpleMessageStore in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testReaperReapsAnEmptyGroupAfterConfiguredDelay.
// INT-2833
@Test
public void testReaperReapsAnEmptyGroupAfterConfiguredDelay() throws Exception {
final MessageGroupStore groupStore = new SimpleMessageStore();
AggregatingMessageHandler handler = new AggregatingMessageHandler(group -> group, groupStore);
final List<Message<?>> outputMessages = new ArrayList<Message<?>>();
handler.setOutputChannel((message, timeout) -> {
/*
* Executes when group 'bar' completes normally
*/
outputMessages.add(message);
return true;
});
handler.setReleaseStrategy(group -> group.size() == 1);
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId("bar").build();
handler.handleMessage(message);
handler.setMinimumTimeoutForEmptyGroups(10_000);
assertEquals(1, outputMessages.size());
assertEquals(1, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
groupStore.expireMessageGroups(0);
assertEquals(1, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
handler.setMinimumTimeoutForEmptyGroups(10);
int n = 0;
while (n++ < 200) {
groupStore.expireMessageGroups(0);
if (TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size() > 0) {
Thread.sleep(50);
} else {
break;
}
}
assertTrue(n < 200);
assertEquals(0, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
}
use of org.springframework.integration.store.SimpleMessageStore in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testScheduleRemoveAnEmptyGroupAfterConfiguredDelay.
@Test
public void testScheduleRemoveAnEmptyGroupAfterConfiguredDelay() throws Exception {
final MessageGroupStore groupStore = new SimpleMessageStore();
AggregatingMessageHandler handler = new AggregatingMessageHandler(group -> group, groupStore);
final List<Message<?>> outputMessages = new ArrayList<Message<?>>();
handler.setOutputChannel((message, timeout) -> {
/*
* Executes when group 'bar' completes normally
*/
outputMessages.add(message);
return true;
});
handler.setReleaseStrategy(group -> group.size() == 1);
handler.setMinimumTimeoutForEmptyGroups(100);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
handler.setTaskScheduler(taskScheduler);
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId("bar").build();
handler.handleMessage(message);
assertEquals(1, outputMessages.size());
assertEquals(1, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
Thread.sleep(100);
int n = 0;
while (TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size() > 0 && n++ < 200) {
Thread.sleep(50);
}
assertTrue(n < 200);
assertEquals(0, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size());
}
use of org.springframework.integration.store.SimpleMessageStore in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testInt3483DeadlockOnMessageStoreRemoveMessageGroup.
@Test
public void testInt3483DeadlockOnMessageStoreRemoveMessageGroup() throws InterruptedException {
final AggregatingMessageHandler handler = new AggregatingMessageHandler(new DefaultAggregatingMessageGroupProcessor());
handler.setOutputChannel(new QueueChannel());
QueueChannel discardChannel = new QueueChannel();
handler.setDiscardChannel(discardChannel);
handler.setReleaseStrategy(group -> true);
handler.setExpireGroupsUponTimeout(false);
SimpleMessageStore messageStore = new SimpleMessageStore() {
@Override
public void removeMessageGroup(Object groupId) {
throw new RuntimeException("intentional");
}
};
handler.setMessageStore(messageStore);
handler.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId(1).setSequenceNumber(1).setSequenceSize(2).build());
try {
messageStore.expireMessageGroups(0);
} catch (Exception e) {
// suppress an intentional 'removeMessageGroup' exception
}
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> handler.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId(1).setSequenceNumber(2).setSequenceSize(2).build()));
executorService.shutdown();
/* Previously lock for the groupId hasn't been unlocked from the 'forceComplete', because it wasn't
reachable in case of exception from the BasicMessageGroupStore.removeMessageGroup
*/
assertTrue(executorService.awaitTermination(10, TimeUnit.SECONDS));
/* Since MessageGroup had been marked as 'complete', but hasn't been removed because of exception,
the second message is discarded
*/
Message<?> receive = discardChannel.receive(10000);
assertNotNull(receive);
}
Aggregations