/** * Populate a {@link DelayHandler} to the current integration flow position. * @param groupId the {@code groupId} for delayed messages in the * {@link org.springframework.integration.store.MessageGroupStore}. * @param endpointConfigurer the {@link Consumer} to provide integration endpoint options. * @return the current {@link IntegrationFlowDefinition}. * @see DelayerEndpointSpec */ public B delay(String groupId, Consumer<DelayerEndpointSpec> endpointConfigurer) { return register(new DelayerEndpointSpec(new DelayHandler(groupId)), endpointConfigurer); }
/** * @param defaultDelay the defaultDelay. * @return the endpoint spec. * @see DelayHandler#setDefaultDelay(long) */ public DelayerEndpointSpec defaultDelay(long defaultDelay) { this.handler.setDefaultDelay(defaultDelay); return _this(); }
public DelayerEndpointSpec delayExpression(Expression delayExpression) { this.handler.setDelayExpression(delayExpression); return this; }
protected void rescheduleAt(final Message<?> message, Date startTime) { getTaskScheduler() .schedule(() -> releaseMessage(message), startTime); }
/** * Handles {@link ContextRefreshedEvent} to invoke * {@link #reschedulePersistedMessages} as late as possible after application context * startup. Also it checks {@link #initialized} to ignore other * {@link ContextRefreshedEvent}s which may be published in the 'parent-child' * contexts, e.g. in the Spring-MVC applications. * @param event - {@link ContextRefreshedEvent} which occurs after Application context * is completely initialized. * @see #reschedulePersistedMessages */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { if (event.getApplicationContext().equals(getApplicationContext()) && !this.initialized.getAndSet(true)) { reschedulePersistedMessages(); } }
/** * Checks if 'requestMessage' wasn't delayed before ({@link #releaseMessageAfterDelay} * and {@link DelayHandler.DelayedMessageWrapper}). Than determine 'delay' for * 'requestMessage' ({@link #determineDelayForMessage}) and if {@code delay > 0} * schedules 'releaseMessage' task after 'delay'. * @param requestMessage - the Message which may be delayed. * @return - {@code null} if 'requestMessage' is delayed, otherwise - 'payload' from * 'requestMessage'. * @see #releaseMessage */ @Override protected Object handleRequestMessage(Message<?> requestMessage) { boolean delayed = requestMessage.getPayload() instanceof DelayedMessageWrapper; if (!delayed) { long delay = determineDelayForMessage(requestMessage); if (delay > 0) { releaseMessageAfterDelay(requestMessage, delay); return null; } } // no delay return delayed ? ((DelayedMessageWrapper) requestMessage.getPayload()).getOriginal() : requestMessage; }
public void testReschedulePersistedMessagesOnStartup() throws Exception { MessageGroupStore messageGroupStore = new SimpleMessageStore(); this.delayHandler.setDefaultDelay(2000); this.delayHandler.setMessageStore(messageGroupStore); startDelayerHandler(); Message<?> message = MessageBuilder.withPayload("test").build(); this.delayHandler = new DelayHandler(DELAYER_MESSAGE_GROUP_ID, this.taskScheduler); this.delayHandler.setOutputChannel(output); this.delayHandler.setDefaultDelay(200); this.delayHandler.setMessageStore(messageGroupStore); this.delayHandler.setBeanFactory(mock(BeanFactory.class)); startDelayerHandler();
@Test public void testRescheduleForTheDateDelay() throws Exception { this.delayHandler.setDelayExpression(new SpelExpressionParser().parseExpression("payload")); this.delayHandler.setOutputChannel(new DirectChannel()); this.delayHandler.setIgnoreExpressionFailures(false); startDelayerHandler(); Calendar releaseDate = Calendar.getInstance(); releaseDate.add(Calendar.HOUR, 1); this.delayHandler.handleMessage(new GenericMessage<>(releaseDate.getTime())); dfa.setPropertyValue("requestDate", requestDate.getTimeInMillis()); this.taskScheduler.afterPropertiesSet(); this.delayHandler.reschedulePersistedMessages(); Queue<?> works = TestUtils.getPropertyValue(this.taskScheduler, "scheduledExecutor.workQueue", Queue.class); int n = 0;
/** * Used for reading persisted Messages in the 'messageStore' to reschedule them e.g. * upon application restart. The logic is based on iteration over * {@code messageGroup.getMessages()} and schedules task for 'delay' logic. This * behavior is dictated by the avoidance of invocation thread overload. */ @Override public synchronized void reschedulePersistedMessages() { MessageGroup messageGroup = this.messageStore.getMessageGroup(this.messageGroupId); for (final Message<?> message : messageGroup.getMessages()) { getTaskScheduler() .schedule(() -> { // This is fine to keep the reference to the message, // because the scheduled task is performed immediately. long delay = determineDelayForMessage(message); if (delay > 0) { releaseMessageAfterDelay(message, delay); } else { releaseMessage(message); } }, new Date()); } }
@Test public void errorFlowAndRetries() throws Exception { delayHandler.setDefaultDelay(10); delayHandler.setRetryDelay(15); startDelayerHandler(); Message<?> message = MessageBuilder.withPayload("test") final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger count = new AtomicInteger(); delayHandler.setDelayedMessageErrorChannel((m, t) -> { count.incrementAndGet(); int deliveries = StaticMessageHeaderAccessor.getDeliveryAttempt(m).get(); delayHandler.setOutputChannel((m, t) -> { throw new MessagingException(m); });
private B delay(String groupId, Expression expression, Consumer<DelayerEndpointSpec> endpointConfigurer) { DelayHandler delayHandler = new DelayHandler(groupId); if (expression != null) { delayHandler.setDelayExpression(expression); } return this.register(new DelayerEndpointSpec(delayHandler), endpointConfigurer); }
private void releaseMessageAfterDelay(final Message<?> message, long delay) { Message<?> delayedMessage = message; DelayedMessageWrapper messageWrapper = null; if (message.getPayload() instanceof DelayedMessageWrapper) { messageWrapper = (DelayedMessageWrapper) message.getPayload(); } else { messageWrapper = new DelayedMessageWrapper(message, System.currentTimeMillis()); delayedMessage = getMessageBuilderFactory() .withPayload(messageWrapper) .copyHeaders(message.getHeaders()) .build(); this.messageStore.addMessageToGroup(this.messageGroupId, delayedMessage); } Runnable releaseTask; if (this.messageStore instanceof SimpleMessageStore) { final Message<?> messageToSchedule = delayedMessage; releaseTask = () -> releaseMessage(messageToSchedule); } else { final UUID messageId = delayedMessage.getHeaders().getId(); releaseTask = () -> { Message<?> messageToRelease = getMessageById(messageId); if (messageToRelease != null) { releaseMessage(messageToRelease); } }; } getTaskScheduler().schedule(releaseTask, new Date(messageWrapper.getRequestDate() + delay)); }
@Test //INT-3560 /* It's difficult to test it from real ctx, because any async process from 'inbound-channel-adapter' can't achieve the DelayHandler before the main thread emits 'ContextRefreshedEvent'. */ public void testRescheduleAndHandleAtTheSameTime() { QueueChannel results = new QueueChannel(); delayHandler.setOutputChannel(results); this.delayHandler.setDefaultDelay(10); startDelayerHandler(); this.input.send(new GenericMessage<>("foo")); this.delayHandler.reschedulePersistedMessages(); Message<?> message = results.receive(10000); assertNotNull(message); message = results.receive(50); assertNull(message); }
public final void handleMessage(final Message<?> message) { long delay = this.determineDelayForMessage(message); if (delay > 0) { this.releaseMessageAfterDelay(message, delay); } else { // no delay, release directly this.releaseMessage(message); } }
/** * @param messageStore the message store. * @return the endpoint spec. */ public DelayerEndpointSpec messageStore(MessageGroupStore messageStore) { this.handler.setMessageStore(messageStore); return _this(); }
/** * @param ignoreExpressionFailures the ignoreExpressionFailures. * @return the endpoint spec. * @see DelayHandler#setIgnoreExpressionFailures(boolean) */ public DelayerEndpointSpec ignoreExpressionFailures(boolean ignoreExpressionFailures) { this.handler.setIgnoreExpressionFailures(ignoreExpressionFailures); return _this(); }
@Before public void setup() { input.setBeanName("input"); output.setBeanName("output"); taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.afterPropertiesSet(); delayHandler = new DelayHandler(DELAYER_MESSAGE_GROUP_ID, taskScheduler); delayHandler.setOutputChannel(output); delayHandler.setBeanFactory(mock(BeanFactory.class)); input.subscribe(delayHandler); output.subscribe(resultHandler); }
@Test public void verifyShutdownWithoutWaitingByDefault() throws Exception { delayHandler.setDefaultDelay(5000); startDelayerHandler(); delayHandler.handleMessage(new GenericMessage<>("foo")); taskScheduler.destroy(); final CountDownLatch latch = new CountDownLatch(1); new Thread(() -> { try { taskScheduler.getScheduledExecutor().awaitTermination(10000, TimeUnit.MILLISECONDS); latch.countDown(); } catch (InterruptedException e) { // won't countDown } }).start(); assertTrue(latch.await(10, TimeUnit.SECONDS)); }
DelayerEndpointSpec(DelayHandler delayHandler) { super(delayHandler); Assert.notNull(delayHandler, "'delayHandler' must not be null."); this.handler.setDelayedAdviceChain(this.delayedAdvice); }
@Test(expected = MessageHandlingException.class) public void testInt2243IgnoreExpressionFailuresAsFalse() { this.setDelayExpression(); this.delayHandler.setIgnoreExpressionFailures(false); startDelayerHandler(); this.delayHandler.handleMessage(new GenericMessage<>("test")); }