/** * Build a master {@link TaskletStep}. * * @return the configured master step * @see RemoteChunkHandlerFactoryBean */ public TaskletStep build() { Assert.notNull(this.inputChannel, "An InputChannel must be provided"); Assert.state(this.outputChannel == null || this.messagingTemplate == null, "You must specify either an outputChannel or a messagingTemplate but not both."); // configure messaging template if (this.messagingTemplate == null) { this.messagingTemplate = new MessagingTemplate(); this.messagingTemplate.setDefaultChannel(this.outputChannel); if (this.logger.isDebugEnabled()) { this.logger.debug("No messagingTemplate was provided, using a default one"); } } // configure item writer ChunkMessageChannelItemWriter<O> chunkMessageChannelItemWriter = new ChunkMessageChannelItemWriter<>(); chunkMessageChannelItemWriter.setMessagingOperations(this.messagingTemplate); chunkMessageChannelItemWriter.setMaxWaitTimeouts(this.maxWaitTimeouts); chunkMessageChannelItemWriter.setThrottleLimit(this.throttleLimit); chunkMessageChannelItemWriter.setReplyChannel(this.inputChannel); super.writer(chunkMessageChannelItemWriter); return super.build(); }
/** * Wait until all the results that are in the pipeline come back to the reply channel. * * @return true if successfully received a result, false if timed out */ private boolean waitForResults() throws AsynchronousFailureException { int count = 0; int maxCount = maxWaitTimeouts; Throwable failure = null; logger.info("Waiting for " + localState.getExpecting() + " results"); while (localState.getExpecting() > 0 && count++ < maxCount) { try { getNextResult(); } catch (Throwable t) { logger.error("Detected error in remote result. Trying to recover " + localState.getExpecting() + " outstanding results before completing.", t); failure = t; } } if (failure != null) { throw wrapIfNecessary(failure); } return count < maxCount; }
try { logger.debug("Waiting for results in step listener..."); timedOut = !waitForResults(); logger.debug("Finished waiting for results in step listener."); for (StepContribution contribution : getStepContributions()) { stepExecution.apply(contribution);
@Before public void setUp() { jobRepository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(), new MapStepExecutionDao(), new MapExecutionContextDao()); factory.setJobRepository(jobRepository); factory.setTransactionManager(new ResourcelessTransactionManager()); factory.setBeanName("step"); factory.setItemWriter(writer); factory.setCommitInterval(4); MessagingTemplate gateway = new MessagingTemplate(); writer.setMessagingOperations(gateway); gateway.setDefaultChannel(requests); writer.setReplyChannel(replies); gateway.setReceiveTimeout(100); TestItemWriter.count = 0; // Drain queues Message<?> message = replies.receive(10); while (message != null) { System.err.println(message); message = replies.receive(10); } }
@Test public void testSimulatedRestartWithNoBacklog() throws Exception { factory.setItemReader(new ListItemReader<>(Arrays.asList(StringUtils .commaDelimitedListToStringArray("1,2,3,4,5,6")))); Step step = factory.getObject(); StepExecution stepExecution = getStepExecution(step); // Set up expectation of three messages (chunks) in the backlog stepExecution.getExecutionContext().putInt(ChunkMessageChannelItemWriter.EXPECTED, 6); stepExecution.getExecutionContext().putInt(ChunkMessageChannelItemWriter.ACTUAL, 3); writer.setMaxWaitTimeouts(2); /* * With no backlog we process all the items, but the listener can't * reconcile the expected number of items with the actual. An infinite * loop would be bad, so the best we can do is fail as fast as possible. */ step.execute(stepExecution); assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution.getExitStatus().getExitCode()); String message = stepExecution.getExitStatus().getExitDescription(); assertTrue("Message did not contain 'timed out': " + message, message.toLowerCase().contains("timed out")); assertEquals(0, TestItemWriter.count); assertEquals(0, stepExecution.getReadCount()); }
@Test public void testUpdateAndOpenWithState() throws Exception { ExecutionContext executionContext = new ExecutionContext(); writer.update(executionContext); writer.open(executionContext); assertEquals(0, executionContext.getInt(ChunkMessageChannelItemWriter.EXPECTED)); assertEquals(0, executionContext.getInt(ChunkMessageChannelItemWriter.ACTUAL)); }
public void write(List<? extends T> items) throws Exception { // Block until expecting <= throttle limit while (localState.getExpecting() > throttleLimit) { getNextResult(); } if (!items.isEmpty()) { ChunkRequest<T> request = localState.getRequest(items); if (logger.isDebugEnabled()) { logger.debug("Dispatching chunk: " + request); } messagingGateway.send(new GenericMessage<>(request)); localState.incrementExpected(); } }
@Test public void testOpenWithNoState() throws Exception { writer.open(new ExecutionContext()); }
@Test public void testSimulatedRestartWithBadMessagesFromAnotherJob() throws Exception { factory.setItemReader(new ListItemReader<>(Arrays.asList(StringUtils .commaDelimitedListToStringArray("1,2,3,4,5,6")))); Step step = factory.getObject(); StepExecution stepExecution = getStepExecution(step); // Set up context with two messages (chunks) in the backlog stepExecution.getExecutionContext().putInt(ChunkMessageChannelItemWriter.EXPECTED, 3); stepExecution.getExecutionContext().putInt(ChunkMessageChannelItemWriter.ACTUAL, 2); // Speed up the eventual failure writer.setMaxWaitTimeouts(2); // And make the back log real requests.send(getSimpleMessage("foo", 4321L)); step.execute(stepExecution); assertEquals(BatchStatus.FAILED, stepExecution.getStatus()); assertEquals(ExitStatus.FAILED.getExitCode(), stepExecution.getExitStatus().getExitCode()); String message = stepExecution.getExitStatus().getExitDescription(); assertTrue("Message does not contain 'wrong job': " + message, message.contains("wrong job")); waitForResults(1, 10); assertEquals(1, TestItemWriter.count); assertEquals(0, stepExecution.getReadCount()); }
@Bean public ChunkMessageChannelItemWriter chunkWriter() { ChunkMessageChannelItemWriter chunkWriter = new ChunkMessageChannelItemWriter(); chunkWriter.setMessagingOperations(messageTemplate()); chunkWriter.setReplyChannel(inboundReplies()); chunkWriter.setMaxWaitTimeouts(10); return chunkWriter; }