diff --git a/spring-cloud-task-integration-tests/pom.xml b/spring-cloud-task-integration-tests/pom.xml index 26ce24b3..cfecb06f 100644 --- a/spring-cloud-task-integration-tests/pom.xml +++ b/spring-cloud-task-integration-tests/pom.xml @@ -46,6 +46,21 @@ org.springframework.cloud spring-cloud-deployer-resource-support + + org.springframework.cloud + spring-cloud-deployer-spi + test + + + org.springframework.cloud + spring-cloud-deployer-local + test + + + com.h2database + h2 + test + diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java new file mode 100644 index 00000000..1bd460dc --- /dev/null +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java @@ -0,0 +1,181 @@ +/* + * Copyright 2016 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.cloud.task.launcher; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import javax.sql.DataSource; + +import org.h2.tools.Server; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.cloud.deployer.spi.local.LocalDeployerProperties; +import org.springframework.cloud.deployer.spi.local.LocalTaskLauncher; +import org.springframework.cloud.deployer.spi.task.TaskLauncher; +import org.springframework.cloud.stream.annotation.Bindings; +import org.springframework.cloud.stream.messaging.Sink; +import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; +import org.springframework.cloud.task.launcher.util.TaskLauncherSinkApplication; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskExplorer; +import org.springframework.cloud.task.repository.support.SimpleTaskExplorer; +import org.springframework.cloud.task.repository.support.TaskExecutionDaoFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.SocketUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = {TaskLauncherSinkApplication.class, TaskLauncherSinkTests.TaskLauncherConfiguration.class}) +public class TaskLauncherSinkTests { + + private final static int WAIT_INTERVAL = 500; + private final static int MAX_WAIT_TIME = 5000; + private final static String URL = "maven://org.springframework.cloud.task.app:" + + "timestamp-task:jar:1.0.0.BUILD-SNAPSHOT"; + private final static String DATASOURCE_URL; + private final static String DATASOURCE_USER_NAME = "SA"; + private final static String DATASOURCE_USER_PASSWORD = ""; + private final static String DATASOURCE_DRIVER_CLASS_NAME = "org.h2.Driver"; + + @ClassRule + public static RabbitTestSupport rabbitTestSupport = new RabbitTestSupport(); + + + @Autowired + @Bindings(TaskLauncherSink.class) + private Sink sink; + + @Autowired + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + taskExplorer = new SimpleTaskExplorer(new TaskExecutionDaoFactoryBean(dataSource)); + } + + private static int randomPort; + + static { + randomPort = SocketUtils.findAvailableTcpPort(); + DATASOURCE_URL = "jdbc:h2:tcp://localhost:" + randomPort + "/mem:dataflow;DB_CLOSE_DELAY=-1;" + + "DB_CLOSE_ON_EXIT=FALSE"; + } + + private DataSource dataSource; + + private Map properties; + + private TaskExplorer taskExplorer; + + @Before + public void setup() { + properties = new HashMap<>(); + properties.put("server.port", "0"); + properties.put("spring.datasource.url", DATASOURCE_URL); + properties.put("spring.datasource.username", DATASOURCE_USER_NAME); + properties.put("spring.datasource.password", DATASOURCE_USER_PASSWORD); + properties.put("spring.datasource.driverClassName", DATASOURCE_DRIVER_CLASS_NAME); + } + + @Test + public void testWithLocalDeployer() throws Exception { + launchTask(URL); + assertTrue(waitForDBToBePopulated()); + + Page taskExecutions = taskExplorer.findAll(new PageRequest(0, 10)); + assertEquals("Only one row is expected", 1, taskExecutions.getTotalElements()); + assertEquals("return code should be 0", 0, taskExecutions.iterator().next().getExitCode().intValue()); + } + + private boolean tableExists() throws SQLException { + boolean result = false; + try ( + Connection conn = dataSource.getConnection(); + ResultSet res = conn.getMetaData().getTables(null, null, "TASK_EXECUTION", + new String[]{"TABLE"});) { + result = res.next(); + } + return result; + } + + private boolean waitForDBToBePopulated() throws Exception { + boolean isDbPopulated = false; + for (int waitTime = 0; waitTime <= MAX_WAIT_TIME; waitTime += WAIT_INTERVAL) { + Thread.sleep(WAIT_INTERVAL); + if (tableExists() && taskExplorer.getTaskExecutionCount() > 0) { + isDbPopulated = true; + break; + } + } + return isDbPopulated; + } + + private void launchTask(String artifactURL) { + TaskLaunchRequest request = new TaskLaunchRequest(artifactURL, null, this.properties, null); + GenericMessage message = new GenericMessage<>(request); + this.sink.input().send(message); + } + + @Configuration + public static class TaskLauncherConfiguration { + @Bean + public TaskLauncher taskLauncher() { + LocalDeployerProperties props = new LocalDeployerProperties(); + props.setDeleteFilesOnExit(false); + + return new LocalTaskLauncher(props); + } + + @Bean(destroyMethod = "stop") + public Server initH2TCPServer() { + Server server = null; + try { + server = Server.createTcpServer("-tcp", "-tcpAllowOthers", "-tcpPort", + String.valueOf(randomPort)).start(); + } + catch (SQLException e) { + throw new IllegalStateException(e); + } + return server; + } + + @Bean + public DataSource dataSource() { + DriverManagerDataSource dataSource = new DriverManagerDataSource(); + dataSource.setDriverClassName(DATASOURCE_DRIVER_CLASS_NAME); + dataSource.setUrl(DATASOURCE_URL); + dataSource.setUsername(DATASOURCE_USER_NAME); + dataSource.setPassword(DATASOURCE_USER_PASSWORD); + return dataSource; + } + } + +} diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java new file mode 100644 index 00000000..141d7eb3 --- /dev/null +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/launcher/util/TaskLauncherSinkApplication.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 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.cloud.task.launcher.util; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.task.launcher.annotation.EnableTaskLauncher; + +/** + * @author Glenn Renfro + */ +@SpringBootApplication +@EnableTaskLauncher +public class TaskLauncherSinkApplication { + + public static void main(String[] args) { + SpringApplication.run(TaskLauncherSinkApplication.class, args); + } +} diff --git a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/listener/BatchExecutionEventTests.java b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/listener/BatchExecutionEventTests.java index 9780051b..6ae41d13 100644 --- a/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/listener/BatchExecutionEventTests.java +++ b/spring-cloud-task-integration-tests/src/test/java/org/springframework/cloud/task/listener/BatchExecutionEventTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.task.listener; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -27,15 +28,11 @@ import org.junit.ClassRule; import org.junit.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.stream.annotation.EnableBinding; import org.springframework.cloud.stream.annotation.StreamListener; -import org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration; import org.springframework.cloud.stream.messaging.Sink; import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; -import org.springframework.cloud.task.batch.configuration.TaskBatchAutoConfiguration; import org.springframework.cloud.task.batch.listener.BatchEventAutoConfiguration; import org.springframework.cloud.task.batch.listener.support.JobExecutionEvent; import org.springframework.cloud.task.batch.listener.support.StepExecutionEvent; @@ -253,12 +250,6 @@ public class BatchExecutionEventTests { private Object[] getConfigurations(Class sinkClazz, Class jobConfigurationClazz) { return new Object[]{ jobConfigurationClazz, - PropertyPlaceholderAutoConfiguration.class, - BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class, - TaskEventAutoConfiguration.class, - BatchEventAutoConfiguration.class, - RabbitServiceAutoConfiguration.class, sinkClazz }; } @@ -268,6 +259,7 @@ public class BatchExecutionEventTests { "--spring.main.web-environment=false", "--spring.cloud.stream.defaultBinder=rabbit", "--spring.cloud.stream.bindings.task-events.destination=test", + "foo=" + UUID.randomUUID().toString(), sinkChannelParam }; } diff --git a/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java index 2ea7f6eb..fb7be7df 100644 --- a/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java +++ b/spring-cloud-task-stream/src/test/java/org/springframework/cloud/task/launcher/TaskLauncherSinkTests.java @@ -35,6 +35,7 @@ import org.springframework.cloud.stream.test.junit.rabbit.RabbitTestSupport; import org.springframework.cloud.task.launcher.configuration.TaskConfiguration; import org.springframework.cloud.task.launcher.util.TaskLauncherSinkApplication; import org.springframework.context.ApplicationContext; +import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.support.GenericMessage; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -48,6 +49,12 @@ public class TaskLauncherSinkTests { private final static String PARAM2 = "BAR"; + private final static String VALID_URL = "maven://org.springframework.cloud.task.app:" + + "timestamp-task:jar:1.0.0.BUILD-SNAPSHOT"; + + private final static String INVALID_URL = "maven://not.real.group:" + + "invalid:jar:1.0.0.BUILD-SNAPSHOT"; + private Map properties; @ClassRule @@ -75,7 +82,7 @@ public class TaskLauncherSinkTests { commandLineArgs.add(PARAM1); commandLineArgs.add(PARAM2); - TaskConfiguration.TestTaskLauncher testTaskLauncher = launchTask(commandLineArgs); + TaskConfiguration.TestTaskLauncher testTaskLauncher = launchTask(VALID_URL, commandLineArgs); assertEquals(LaunchState.complete, testTaskLauncher.status(DEFAULT_STATUS).getState()); assertEquals(2, testTaskLauncher.getCommandlineArguments().size()); @@ -85,11 +92,16 @@ public class TaskLauncherSinkTests { @Test public void testSuccessNoParams() { - TaskConfiguration.TestTaskLauncher testTaskLauncher= launchTask(null); + TaskConfiguration.TestTaskLauncher testTaskLauncher = launchTask(VALID_URL, null); assertEquals(LaunchState.complete, testTaskLauncher.status(DEFAULT_STATUS).getState()); assertEquals(0, testTaskLauncher.getCommandlineArguments().size()); } + @Test(expected = MessageHandlingException.class) + public void testInvalidJar() { + TaskConfiguration.TestTaskLauncher testTaskLauncher = launchTask(INVALID_URL, null); + } + @Test public void testNoRun() { TaskConfiguration.TestTaskLauncher testTaskLauncher = @@ -101,16 +113,14 @@ public class TaskLauncherSinkTests { @Test(expected = IllegalArgumentException.class) public void testNoTaskLauncher() { TaskLauncherSink sink = new TaskLauncherSink(); - sink.taskLauncherSink(new TaskLaunchRequest("maven://org.springframework.cloud.task.app:" - + "timestamp-task:jar:1.0.0.BUILD-SNAPSHOT",null, properties, null)); + sink.taskLauncherSink(new TaskLaunchRequest(VALID_URL, null, properties, null)); } - private TaskConfiguration.TestTaskLauncher launchTask(List commandLineArgs) { + private TaskConfiguration.TestTaskLauncher launchTask(String artifactURL, List commandLineArgs) { TaskConfiguration.TestTaskLauncher testTaskLauncher = context.getBean(TaskConfiguration.TestTaskLauncher.class); - TaskLaunchRequest request = new TaskLaunchRequest("maven://org.springframework.cloud.task.app:" - + "timestamp-task:jar:1.0.0.BUILD-SNAPSHOT",commandLineArgs, properties, null); + TaskLaunchRequest request = new TaskLaunchRequest(artifactURL, commandLineArgs, properties, null); GenericMessage message = new GenericMessage<>(request); this.sink.input().send(message); return testTaskLauncher;