BATCHADM-161 - Add Job Launching Gateway

For reference see: https://jira.springsource.org/browse/BATCHADM-161

* Add XML Schema
* Add Namespace Handler
* Add Parser and Gateway classes
* Add tests
This commit is contained in:
Gunnar Hillert
2013-05-01 12:47:45 -04:00
committed by Michael Minella
parent f23dd64740
commit 3909b75151
20 changed files with 904 additions and 4 deletions

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.batch.integration.config.xml;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Configuration;
/**
*
* @author Gunnar Hillert
* @since 1.3
*
*/
@Configuration
@EnableBatchProcessing
public class JobLauncherParserTestsConfiguration {
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.batch.integration.config.xml;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Test;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.integration.launch.JobLaunchingMessageHandler;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.test.util.TestUtils;
/**
*
* @author Gunnar Hillert
* @since 1.3
*
*/
public class JobLaunchingGatewayParserTests {
private ConfigurableApplicationContext context;
private EventDrivenConsumer consumer;
@Test
public void testGatewayParser() throws Exception {
setUp("JobLaunchingGatewayParserTests-context.xml", getClass());
final AbstractMessageChannel inputChannel = TestUtils.getPropertyValue(this.consumer, "inputChannel", AbstractMessageChannel.class);
assertEquals("requestChannel", inputChannel.getComponentName());
final JobLaunchingMessageHandler jobLaunchingMessageHandler = TestUtils.getPropertyValue(this.consumer, "handler.jobLaunchingMessageHandler", JobLaunchingMessageHandler.class);
assertNotNull(jobLaunchingMessageHandler);
final MessagingTemplate messagingTemplate = TestUtils.getPropertyValue(this.consumer, "handler.messagingTemplate", MessagingTemplate.class);
final Long sendTimeout = TestUtils.getPropertyValue(messagingTemplate, "sendTimeout", Long.class);
assertEquals("Wrong sendTimeout", Long.valueOf(123L), sendTimeout);
assertFalse(this.consumer.isRunning());
}
@Test
public void testJobLaunchingGatewayIsRunning() throws Exception {
setUp("JobLaunchingGatewayParserTestsRunning-context.xml", getClass());
assertTrue(this.consumer.isRunning());
final MessagingTemplate messagingTemplate = TestUtils.getPropertyValue(this.consumer, "handler.messagingTemplate", MessagingTemplate.class);
final Long sendTimeout = TestUtils.getPropertyValue(messagingTemplate, "sendTimeout", Long.class);
assertEquals("Wrong sendTimeout", Long.valueOf(-1L), sendTimeout);
}
@Test
public void testJobLaunchingGatewayNoJobLauncher() throws Exception {
try {
setUp("JobLaunchingGatewayParserTestsNoJobLauncher-context.xml", getClass());
}
catch(BeanCreationException e) {
assertEquals("No bean named 'jobLauncher' is defined", e.getCause().getMessage());
return;
}
fail("Expected a NoSuchBeanDefinitionException to be thrown.");
}
@Test
public void testJobLaunchingGatewayWithEnableBatchProcessing() throws Exception {
setUp("JobLaunchingGatewayParserTestsWithEnableBatchProcessing-context.xml", getClass());
final JobLaunchingMessageHandler jobLaunchingMessageHandler = TestUtils.getPropertyValue(this.consumer, "handler.jobLaunchingMessageHandler", JobLaunchingMessageHandler.class);
assertNotNull(jobLaunchingMessageHandler);
final JobLauncher jobLauncher = TestUtils.getPropertyValue(jobLaunchingMessageHandler, "jobLauncher", JobLauncher.class);
assertNotNull(jobLauncher);
}
@After
public void tearDown(){
if(context != null){
context.close();
}
}
public void setUp(String name, Class<?> cls){
context = new ClassPathXmlApplicationContext(name, cls);
consumer = this.context.getBean("batchjobExecutor", EventDrivenConsumer.class);
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.integration.launch;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.integration.JobSupport;
import org.springframework.batch.integration.step.TestTasklet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* @author Gunnar Hillert
* @since 1.3
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class JobLaunchingGatewayIntegrationTests {
@Autowired
@Qualifier("requests")
private MessageChannel requestChannel;
@Autowired
@Qualifier("response")
private PollableChannel responseChannel;
@Autowired
private TestTasklet tasklet;
@Autowired
private Job testJob;
private JobSupport job = new JobSupport("testJob");
@Before
public void setUp() {
Object message = "";
while (message!=null) {
message = responseChannel.receive(10L);
}
}
@Test
@DirtiesContext
@SuppressWarnings("unchecked")
public void testNoReply() {
GenericMessage<JobLaunchRequest> trigger = new GenericMessage<JobLaunchRequest>(new JobLaunchRequest(job,
new JobParameters()));
try {
requestChannel.send(trigger);
}
catch (MessagingException e) {
String message = e.getMessage();
assertTrue("Wrong message: " + message, message.contains("replyChannel"));
}
Message<JobExecution> executionMessage = (Message<JobExecution>) responseChannel.receive(1000);
assertNull("JobExecution message received when no return address set", executionMessage);
}
@SuppressWarnings("unchecked")
@Test
@DirtiesContext
public void testReply() {
JobParametersBuilder builder = new JobParametersBuilder();
builder.addString("dontclash", "12");
Map<String, Object> map = new HashMap<String, Object>();
map.put(MessageHeaders.REPLY_CHANNEL, "response");
MessageHeaders headers = new MessageHeaders(map);
GenericMessage<JobLaunchRequest> trigger = new GenericMessage<JobLaunchRequest>(new JobLaunchRequest(job,
builder.toJobParameters()), headers);
requestChannel.send(trigger);
Message<JobExecution> executionMessage = (Message<JobExecution>) responseChannel.receive(1000);
assertNotNull("No response received", executionMessage);
JobExecution execution = executionMessage.getPayload();
assertNotNull("JobExecution not returned", execution);
}
@Test
@DirtiesContext
@SuppressWarnings("unchecked")
public void testWrongPayload() {
final Message<String> stringMessage = MessageBuilder.withPayload("just text").build();
try {
requestChannel.send(stringMessage);
}
catch (MessageHandlingException e) {
String message = e.getCause().getMessage();
assertTrue("Wrong message: " + message, message.contains("The payload must be of type JobLaunchRequest."));
}
Message<JobExecution> executionMessage = (Message<JobExecution>) responseChannel.receive(1000);
assertNull("JobExecution message received when no return address set", executionMessage);
}
@Test
@DirtiesContext
@SuppressWarnings("unchecked")
public void testExceptionRaised() throws InterruptedException {
this.tasklet.setFail(true);
JobParametersBuilder builder = new JobParametersBuilder();
builder.addString("dontclash", "12");
Map<String, Object> map = new HashMap<String, Object>();
map.put(MessageHeaders.REPLY_CHANNEL, "response");
MessageHeaders headers = new MessageHeaders(map);
GenericMessage<JobLaunchRequest> trigger = new GenericMessage<JobLaunchRequest>(new JobLaunchRequest(testJob,
builder.toJobParameters()), headers);
requestChannel.send(trigger);
Message<JobExecution> executionMessage = (Message<JobExecution>) responseChannel.receive(1000);
assertNotNull("No response received", executionMessage);
JobExecution execution = executionMessage.getPayload();
assertNotNull("JobExecution not returned", execution);
assertEquals(ExitStatus.FAILED.getExitCode(), execution.getExitStatus().getExitCode());
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.integration.launch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.integration.JobSupport;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.support.MessageBuilder;
/**
*
* @author Gunnar Hillert
* @since 1.3
*
*/
public class JobLaunchingGatewayTests {
@Test
public void testExceptionRaised() throws Exception {
final Message<JobLaunchRequest> message = MessageBuilder.withPayload(new JobLaunchRequest(new JobSupport("testJob"),
new JobParameters())).build();
final JobLauncher jobLauncher = mock(JobLauncher.class);
when(jobLauncher.run(any(Job.class), any(JobParameters.class)))
.thenThrow(new JobParametersInvalidException("This is a JobExecutionException."));
JobLaunchingGateway jobLaunchingGateway = new JobLaunchingGateway(jobLauncher);
try {
jobLaunchingGateway.handleMessage(message);
}
catch (MessageHandlingException e) {
assertEquals("This is a JobExecutionException.", e.getCause().getMessage());
return;
}
fail("Expecting a MessageHandlingException to be thrown.");
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<import resource="batch-setup-context.xml"/>
<int:channel id="requestChannel"/>
<int:channel id="replyChannel">
<int:queue/>
</int:channel>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
<batch-int:job-launching-gateway id="batchjobExecutor" reply-timeout="123"
auto-startup="false"
request-channel="requestChannel"
reply-channel="replyChannel"
job-launcher="jobLauncher"/>
</beans>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<import resource="batch-setup-context.xml"/>
<int:channel id="requestChannel"/>
<int:channel id="replyChannel">
<int:queue/>
</int:channel>
<batch-int:job-launching-gateway id="batchjobExecutor"
auto-startup="true"
request-channel="requestChannel"
reply-channel="replyChannel"/>
</beans>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<import resource="batch-setup-context.xml"/>
<int:channel id="requestChannel"/>
<int:channel id="replyChannel">
<int:queue/>
</int:channel>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository"/>
</bean>
<batch-int:job-launching-gateway id="batchjobExecutor"
auto-startup="true"
request-channel="requestChannel"
reply-channel="replyChannel"
job-launcher="jobLauncher"/>
</beans>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<context:annotation-config />
<bean class="org.springframework.batch.integration.config.xml.JobLauncherParserTestsConfiguration"/>
<int:channel id="requestChannel"/>
<int:channel id="replyChannel">
<int:queue/>
</int:channel>
<batch-int:job-launching-gateway id="batchjobExecutor"
auto-startup="true"
request-channel="requestChannel"
reply-channel="replyChannel"/>
</beans>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:batch-int="http://www.springframework.org/schema/batch-integration"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<jdbc:embedded-database id="dataSource" type="HSQL"/>
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:/org/springframework/batch/core/schema-drop-hsqldb.sql"/>
<jdbc:script location="classpath:/org/springframework/batch/core/schema-hsqldb.sql"/>
</jdbc:initialize-database>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<batch:job-repository data-source="dataSource" id="jobRepository"
transaction-manager="transactionManager" table-prefix="BATCH_"/>
</beans>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch-integration="http://www.springframework.org/schema/batch-integration"
xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/batch-integration http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<import resource="classpath:simple-job-launcher-context.xml" />
<integration:annotation-config/>
<integration:channel id="requests" />
<integration:channel id="response">
<integration:queue/>
</integration:channel>
<batch-integration:job-launching-gateway request-channel="requests" job-launcher="jobLauncher" />
<bean id="testJob" parent="simpleJob">
<property name="steps" ref="step" />
</bean>
<bean id="step" parent="taskletStep">
<property name="tasklet" ref="tasklet"/>
</bean>
<bean id="tasklet" class="org.springframework.batch.integration.step.TestTasklet" />
</beans>