Repository: spring-cloud/spring-cloud-dataflow
Issue: SCDF fails to start with Scheduling disabled

As a user, when deploying SCDF to CF without Scheduling (i.e., `SPRING_CLOUD_DATAFLOW_FEATURES_SCHEDULES_ENABLED=false`), I get the following error on `master` and on `2.3 M2`.

```
2019-10-07T21:36:17.35-0700 [APP/PROC/WEB/0] OUT ***************************
   2019-10-07T21:36:17.35-0700 [APP/PROC/WEB/0] OUT APPLICATION FAILED TO START
   2019-10-07T21:36:17.35-0700 [APP/PROC/WEB/0] OUT ***************************
   2019-10-07T21:36:17.35-0700 [APP/PROC/WEB/0] OUT Description:
   2019-10-07T21:36:17.35-0700 [APP/PROC/WEB/0] OUT Parameter 8 of method deleteTaskService in org.springframework.cloud.dataflow.server.config.features.TaskConfiguration required a bean of type 'org.springframework.cloud.dataflow.server.service.SchedulerService' that could not be found.
```

The same error also appeared when deploying `master` on K8s, but it could have been due to a corrupted/unstable image.

Changed files:
spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskDeleteService.java
spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java
spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/configuration/TaskServiceDependencies.java
spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/service/impl/DefaultSchedulerServiceTests.java

diff --git a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
index 13752c791..e2f7dd354 100644
--- a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
+++ b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
@@ -32,11 +32,15 @@ import org.junit.Rule;
 import org.mockito.ArgumentMatchers;
 
 import org.springframework.cloud.dataflow.core.ApplicationType;
+import org.springframework.cloud.dataflow.core.Launcher;
+import org.springframework.cloud.dataflow.core.TaskPlatform;
 import org.springframework.cloud.dataflow.server.controller.TaskSchedulerController;
 import org.springframework.cloud.dataflow.server.service.SchedulerService;
 import org.springframework.cloud.dataflow.server.single.LocalDataflowResource;
 import org.springframework.cloud.deployer.spi.app.AppDeployer;
 import org.springframework.cloud.deployer.spi.scheduler.ScheduleInfo;
+import org.springframework.cloud.deployer.spi.scheduler.ScheduleRequest;
+import org.springframework.cloud.deployer.spi.scheduler.Scheduler;
 import org.springframework.cloud.skipper.domain.AboutResource;
 import org.springframework.cloud.skipper.domain.Dependency;
 import org.springframework.cloud.skipper.domain.Deployer;
@@ -129,7 +133,9 @@ public abstract class BaseDocumentation {
 		this.dataSource = springDataflowServer.getWebApplicationContext().getBean(DataSource.class);
 		TaskSchedulerController controller = this.springDataflowServer.getWebApplicationContext().getBean(TaskSchedulerController.class);
 		ReflectionTestUtils.setField(controller, "schedulerService", schedulerService());
-
+		TaskPlatform taskPlatform = this.springDataflowServer.getWebApplicationContext().getBean(TaskPlatform.class);
+		Launcher launcher = taskPlatform.getLaunchers().stream().filter(launcherToFilter -> launcherToFilter.getName().equals("default")).findFirst().get();
+		ReflectionTestUtils.setField(launcher, "scheduler", localTestScheduler());
 	}
 
 	/**
@@ -271,17 +277,41 @@ public abstract class BaseDocumentation {
 			public ScheduleInfo getSchedule(String scheduleName) {
 				return null;
 			}
+		};
+	}
+
+	private List<ScheduleInfo> getSampleList() {
+		List<ScheduleInfo> result = new ArrayList<>();
+		ScheduleInfo scheduleInfo = new ScheduleInfo();
+		scheduleInfo.setScheduleName("FOO");
+		scheduleInfo.setTaskDefinitionName("BAR");
+		Map<String, String> props = new HashMap<>(1);
+		props.put("scheduler.AAA.spring.cloud.scheduler.cron.expression", "00 41 17 ? * *");
+		scheduleInfo.setScheduleProperties(props);
+		result.add(scheduleInfo);
+		return result;
+	}
+
+	public Scheduler localTestScheduler() {
+		return new Scheduler() {
+			@Override
+			public void schedule(ScheduleRequest scheduleRequest) {
+				throw new UnsupportedOperationException("Interface is not implemented for schedule method.");
+			}
 
-			private List<ScheduleInfo> getSampleList() {
-				List<ScheduleInfo> result = new ArrayList<>();
-				ScheduleInfo scheduleInfo = new ScheduleInfo();
-				scheduleInfo.setScheduleName("FOO");
-				scheduleInfo.setTaskDefinitionName("BAR");
-				Map<String, String> props = new HashMap<>(1);
-				props.put("scheduler.AAA.spring.cloud.scheduler.cron.expression", "00 41 17 ? * *");
-				scheduleInfo.setScheduleProperties(props);
-				result.add(scheduleInfo);
-				return result;
+			@Override
+			public void unschedule(String scheduleName) {
+				throw new UnsupportedOperationException("Interface is not implemented for unschedule method.");
+			}
+
+			@Override
+			public List<ScheduleInfo> list(String taskDefinitionName) {
+				throw new UnsupportedOperationException("Interface is not implemented for list method.");
+			}
+
+			@Override
+			public List<ScheduleInfo> list() {
+				return getSampleList();
 			}
 		};
 	}
diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java
index 079e86d18..956148484 100644
--- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java
+++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/features/TaskConfiguration.java
@@ -15,9 +15,7 @@
  */
 package org.springframework.cloud.dataflow.server.config.features;
 
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 
 import javax.sql.DataSource;
 
@@ -61,15 +59,12 @@ import org.springframework.cloud.dataflow.server.service.impl.DefaultTaskJobServ
 import org.springframework.cloud.dataflow.server.service.impl.DefaultTaskSaveService;
 import org.springframework.cloud.dataflow.server.service.impl.TaskAppDeploymentRequestCreator;
 import org.springframework.cloud.dataflow.server.service.impl.TaskConfigurationProperties;
-import org.springframework.cloud.deployer.spi.scheduler.ScheduleInfo;
 import org.springframework.cloud.deployer.spi.scheduler.Scheduler;
 import org.springframework.cloud.task.repository.TaskExplorer;
 import org.springframework.cloud.task.repository.TaskRepository;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Profile;
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
 import org.springframework.data.map.repository.config.EnableMapRepositories;
 import org.springframework.lang.Nullable;
 import org.springframework.transaction.PlatformTransactionManager;
@@ -97,6 +92,9 @@ public class TaskConfiguration {
 	@Autowired
 	DataSourceProperties dataSourceProperties;
 
+	@Autowired(required = false)
+	SchedulerService schedulerService;
+
 	@Value("${spring.cloud.dataflow.server.uri:}")
 	private String dataflowServerUri;
 
@@ -130,55 +128,6 @@ public class TaskConfiguration {
 		return taskPlatform;
 	}
 
-	/**
-	 * The default profile is active when no other profiles are active. This is configured so
-	 * that several tests will pass without having to explicitly enable the local profile.
-	 * @return a no-op {@link SchedulerService}
-	 */
-	@Profile({ "local", "default" })
-	@Bean
-	public SchedulerService schedulerService() {
-		return new SchedulerService() {
-			@Override
-			public void schedule(String scheduleName, String taskDefinitionName, Map<String, String> taskProperties, List<String> commandLineArgs) {
-			}
-
-			@Override
-			public void unschedule(String scheduleName) {
-			}
-
-			@Override
-			public void unscheduleForTaskDefinition(String taskDefinitionName) {
-			}
-
-			@Override
-			public List<ScheduleInfo> list(Pageable pageable, String taskDefinitionName) {
-				return null;
-			}
-
-			@Override
-			public Page<ScheduleInfo> list(Pageable pageable) {
-				return null;
-			}
-
-			@Override
-			public List<ScheduleInfo> list(String taskDefinitionName) {
-				return Collections.emptyList();
-			}
-
-			@Override
-			public List<ScheduleInfo> list() {
-				return Collections.emptyList();
-			}
-
-			@Override
-			public ScheduleInfo getSchedule(String scheduleName) {
-				return null;
-			}
-		};
-	}
-
-
 	@Bean
 	public TaskExecutionInfoService taskDefinitionRetriever(AppRegistryService registry,
 			TaskExplorer taskExplorer, TaskDefinitionRepository taskDefinitionRepository,
@@ -194,15 +143,14 @@ public class TaskConfiguration {
 			AuditRecordService auditRecordService,
 			DataflowTaskExecutionDao dataflowTaskExecutionDao,
 			DataflowJobExecutionDao dataflowJobExecutionDao,
-			DataflowTaskExecutionMetadataDao dataflowTaskExecutionMetadataDao,
-			SchedulerService schedulerService) {
+			DataflowTaskExecutionMetadataDao dataflowTaskExecutionMetadataDao) {
 		return new DefaultTaskDeleteService(taskExplorer, launcherRepository, taskDefinitionRepository,
 				taskDeploymentRepository,
 				auditRecordService,
 				dataflowTaskExecutionDao,
 				dataflowJobExecutionDao,
 				dataflowTaskExecutionMetadataDao,
-				schedulerService);
+				this.schedulerService);
 	}
 
 	@Bean
diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskDeleteService.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskDeleteService.java
index bb3d6907e..6c5c8f489 100644
--- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskDeleteService.java
+++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/service/impl/DefaultTaskDeleteService.java
@@ -114,7 +114,6 @@ public class DefaultTaskDeleteService implements TaskDeleteService {
 		Assert.notNull(dataflowTaskExecutionDao, "DataflowTaskExecutionDao must not be null");
 		Assert.notNull(dataflowJobExecutionDao, "DataflowJobExecutionDao must not be null");
 		Assert.notNull(dataflowTaskExecutionMetadataDao, "DataflowTaskExecutionMetadataDao must not be null");
-		Assert.notNull(schedulerService, "SchedulerService must not be null");
 		this.taskExplorer = taskExplorer;
 		this.launcherRepository = launcherRepository;
 		this.taskDefinitionRepository = taskDefinitionRepository;
@@ -324,7 +323,9 @@ public class DefaultTaskDeleteService implements TaskDeleteService {
 
 	private void deleteTaskDefinition(TaskDefinition taskDefinition) {
 		TaskParser taskParser = new TaskParser(taskDefinition.getName(), taskDefinition.getDslText(), true, true);
-		schedulerService.unscheduleForTaskDefinition(taskDefinition.getTaskName());
+		if (this.schedulerService != null) {
+			schedulerService.unscheduleForTaskDefinition(taskDefinition.getTaskName());
+		}
 		TaskNode taskNode = taskParser.parse();
 		// if composed-task-runner definition then destroy all child tasks associated with it.
 		if (taskNode.isComposed()) {
diff --git a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/configuration/TaskServiceDependencies.java b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/configuration/TaskServiceDependencies.java
index 420a90488..468ae9aab 100644
--- a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/configuration/TaskServiceDependencies.java
+++ b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/configuration/TaskServiceDependencies.java
@@ -45,6 +45,7 @@ import org.springframework.cloud.dataflow.server.DockerValidatorProperties;
 import org.springframework.cloud.dataflow.server.config.VersionInfoProperties;
 import org.springframework.cloud.dataflow.server.config.apps.CommonApplicationProperties;
 import org.springframework.cloud.dataflow.server.config.features.FeaturesProperties;
+import org.springframework.cloud.dataflow.server.config.features.SchedulerConfiguration;
 import org.springframework.cloud.dataflow.server.job.LauncherRepository;
 import org.springframework.cloud.dataflow.server.repository.DataflowJobExecutionDao;
 import org.springframework.cloud.dataflow.server.repository.DataflowTaskExecutionDao;
@@ -82,6 +83,7 @@ import org.springframework.cloud.task.repository.support.SimpleTaskRepository;
 import org.springframework.cloud.task.repository.support.TaskExecutionDaoFactoryBean;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.core.io.Resource;
@@ -104,6 +106,7 @@ import static org.mockito.Mockito.when;
  * @author Glenn Renfro
  * @author David Turanski
  * @author Gunnar Hillert
+ * @author Ilayaperumal Gopinathan
  */
 @Configuration
 @EnableSpringDataWebSupport
@@ -234,7 +237,7 @@ public class TaskServiceDependencies extends WebMvcConfigurationSupport {
 			DataflowTaskExecutionDao dataflowTaskExecutionDao,
 			DataflowJobExecutionDao dataflowJobExecutionDao,
 			DataflowTaskExecutionMetadataDao dataflowTaskExecutionMetadataDao,
-			SchedulerService schedulerService) {
+			@Autowired(required = false) SchedulerService schedulerService) {
 
 		return new DefaultTaskDeleteService(taskExplorer, launcherRepository, taskDefinitionRepository,
 				taskDeploymentRepository,
@@ -296,6 +299,7 @@ public class TaskServiceDependencies extends WebMvcConfigurationSupport {
 	}
 
 	@Bean
+	@Conditional({ SchedulerConfiguration.SchedulerConfigurationPropertyChecker.class })
 	public SchedulerService schedulerService(CommonApplicationProperties commonApplicationProperties,
 											 TaskPlatform taskPlatform, TaskDefinitionRepository taskDefinitionRepository,
 											 AppRegistryService registry, ResourceLoader resourceLoader,
diff --git a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/service/impl/DefaultSchedulerServiceTests.java b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/service/impl/DefaultSchedulerServiceTests.java
index 4fbdd7abb..df558a59f 100644
--- a/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/service/impl/DefaultSchedulerServiceTests.java
+++ b/spring-cloud-dataflow-server-core/src/test/java/org/springframework/cloud/dataflow/server/service/impl/DefaultSchedulerServiceTests.java
@@ -78,7 +78,8 @@ import static org.mockito.Mockito.when;
 		"spring.cloud.dataflow.applicationProperties.task.globalkey=globalvalue",
 		"spring.cloud.dataflow.applicationProperties.stream.globalstreamkey=nothere",
 		"spring.main.allow-bean-definition-overriding=true",
-		"spring.cloud.dataflow.task.scheduler-task-launcher-url=https://test.test"})
+		"spring.cloud.dataflow.task.scheduler-task-launcher-url=https://test.test",
+		"spring.cloud.dataflow.features.schedules-enabled=true"})
 @EnableConfigurationProperties({ CommonApplicationProperties.class, TaskConfigurationProperties.class, DockerValidatorProperties.class})
 @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
 @AutoConfigureTestDatabase(replace = Replace.ANY)