Application that lets users test task/batch scenarios with one app
resolves #140
This commit is contained in:
committed by
Janne Valkealahti
parent
0d0c9b9d73
commit
5cbd231125
@@ -0,0 +1,13 @@
|
||||
package io.spring.scenariotask;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ScenarioTaskApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ScenarioTaskApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2020 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 io.spring.scenariotask.configuration;
|
||||
|
||||
/**
|
||||
* Exception thrown when user requests an error to occur during Task or Batch executions.
|
||||
*
|
||||
* @author Glenn Renfro
|
||||
*/
|
||||
public class ExpectedException extends Exception{
|
||||
public ExpectedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2020 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 io.spring.scenariotask.configuration;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
|
||||
/**
|
||||
* Properties that allow the user to specify how they want the Batch/Task app to behave for a test.
|
||||
*
|
||||
* @author Glenn Renfro
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "io.spring")
|
||||
public class ScenarioProperties {
|
||||
|
||||
/**
|
||||
* The name associated with the batch job. The default is "scenario-job".
|
||||
*/
|
||||
private String jobName = "scenario-job";
|
||||
|
||||
/**
|
||||
* The name associated with the single step for the job. The default is "scenario-step".
|
||||
*/
|
||||
private String stepName = "scenario-step";
|
||||
|
||||
/**
|
||||
* If true, the batch will throw a {@link ExpectedException}. Defaults to false.
|
||||
*/
|
||||
private boolean failBatch;
|
||||
|
||||
/**
|
||||
* If true, the task will throw a {@link ExpectedException}. Defaults to false.
|
||||
*/
|
||||
private boolean failTask;
|
||||
|
||||
/**
|
||||
* If true, the task will launch a sample batch job. Defaults to true.
|
||||
*/
|
||||
private boolean launchBatchJob = true;
|
||||
|
||||
/**
|
||||
* How long the batch job should pause in the step. Defaults to 0.
|
||||
*/
|
||||
private int pauseInSeconds = 0;
|
||||
|
||||
/**
|
||||
* If true a runIdIncrementer will be applied to the batch job. Defaults to false.
|
||||
*/
|
||||
private boolean includeRunidIncrementer;
|
||||
|
||||
public String getJobName() {
|
||||
return jobName;
|
||||
}
|
||||
|
||||
public void setJobName(String jobName) {
|
||||
this.jobName = jobName;
|
||||
}
|
||||
|
||||
public boolean isFailBatch() {
|
||||
return failBatch;
|
||||
}
|
||||
|
||||
public void setFailBatch(boolean failBatch) {
|
||||
this.failBatch = failBatch;
|
||||
}
|
||||
|
||||
public boolean isFailTask() {
|
||||
return failTask;
|
||||
}
|
||||
|
||||
public void setFailTask(boolean failTask) {
|
||||
this.failTask = failTask;
|
||||
}
|
||||
|
||||
public int getPauseInSeconds() {
|
||||
return pauseInSeconds;
|
||||
}
|
||||
|
||||
public void setPauseInSeconds(int pauseInSeconds) {
|
||||
this.pauseInSeconds = pauseInSeconds;
|
||||
}
|
||||
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
public boolean isLaunchBatchJob() {
|
||||
return launchBatchJob;
|
||||
}
|
||||
|
||||
public void setLaunchBatchJob(boolean launchBatchJob) {
|
||||
this.launchBatchJob = launchBatchJob;
|
||||
}
|
||||
|
||||
public boolean isIncludeRunidIncrementer() {
|
||||
return includeRunidIncrementer;
|
||||
}
|
||||
|
||||
public void setIncludeRunidIncrementer(boolean includeRunidIncrementer) {
|
||||
this.includeRunidIncrementer = includeRunidIncrementer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2020 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 io.spring.scenariotask.configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
|
||||
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
|
||||
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.task.configuration.EnableTask;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configure the Task and or Batch components of the test application.
|
||||
*
|
||||
* @author Glenn Renfro
|
||||
*/
|
||||
@EnableTask
|
||||
@EnableBatchProcessing
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ScenarioProperties.class)
|
||||
public class ScenarioTaskConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ScenarioTaskConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
public JobBuilderFactory jobBuilderFactory;
|
||||
|
||||
@Autowired
|
||||
public StepBuilderFactory stepBuilderFactory;
|
||||
|
||||
@Autowired
|
||||
public JobExplorer jobExplorer;
|
||||
|
||||
@Autowired
|
||||
public ScenarioProperties properties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(
|
||||
value = "io.spring.launchBatchJob",
|
||||
havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public Job pausedemoAgain() {
|
||||
SimpleJobBuilder jobBuilder = this.jobBuilderFactory.get(properties.getJobName())
|
||||
.start(this.stepBuilderFactory.get(properties.getStepName())
|
||||
.tasklet((contribution, chunkContext) -> {
|
||||
logger.info(String.format("%s is starting", properties.getStepName()));
|
||||
if (properties.getPauseInSeconds() > 0) {
|
||||
logger.info(String.format("%s is pausing", properties.getStepName()));
|
||||
Thread.sleep(properties.getPauseInSeconds() * 1000);
|
||||
}
|
||||
logger.info(String.format("%s is completing", properties.getStepName()));
|
||||
|
||||
if (jobExecutionCount() == 1 && properties.isFailBatch()) {
|
||||
throw new ExpectedException("Exception thrown during Batch Execution");
|
||||
}
|
||||
return RepeatStatus.FINISHED;
|
||||
})
|
||||
.build());
|
||||
if (this.properties.isIncludeRunidIncrementer()) {
|
||||
jobBuilder.incrementer(new RunIdIncrementer());
|
||||
}
|
||||
return jobBuilder.build();
|
||||
}
|
||||
|
||||
private int jobExecutionCount() {
|
||||
JobInstance jobInstance = jobExplorer.getLastJobInstance(this.properties.getJobName());
|
||||
List<JobExecution> jobExecutions = jobExplorer.getJobExecutions(jobInstance);
|
||||
return jobExecutions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays simple log message. If user specifies {@code io.spring.fail-task=true} a {@link ExpectedException} is thrown.
|
||||
*
|
||||
* @return ApplicationRunner instance for the app.
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationRunner applicationRunner(ScenarioProperties properties) {
|
||||
return args -> {
|
||||
logger.info("ApplicationRunner Executing for ScenarioTaskApplication");
|
||||
if (properties.isFailTask()) {
|
||||
throw new ExpectedException("Exception thrown during Task Execution");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
configuration-properties.classes=io.spring.scenariotask.configuration.ScenarioProperties
|
||||
1
scenario-task/src/main/resources/application.properties
Normal file
1
scenario-task/src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
||||
logging.level.org.springframework.cloud.task=debug
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2020 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 io.spring.scenariotask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.spring.scenariotask.configuration.ExpectedException;
|
||||
import javax.sql.DataSource;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
|
||||
import org.springframework.batch.core.repository.dao.Jackson2ExecutionContextStringSerializer;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.cloud.task.listener.TaskException;
|
||||
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.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@Testcontainers
|
||||
public class ScenarioTaskApplicationTests {
|
||||
|
||||
private static DataSource dataSource;
|
||||
|
||||
private static JobExplorer jobExplorer;
|
||||
|
||||
private static TaskExplorer taskExplorer;
|
||||
|
||||
@Container
|
||||
private static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:11.1")
|
||||
.withDatabaseName("integration-tests-db")
|
||||
.withUsername("sa")
|
||||
.withPassword("sa");
|
||||
|
||||
@BeforeAll
|
||||
public static void initializeDB() throws Exception {
|
||||
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
|
||||
driverManagerDataSource.setDriverClassName("org.postgresql.Driver");
|
||||
driverManagerDataSource.setUrl(postgreSQLContainer.getJdbcUrl());
|
||||
driverManagerDataSource.setUsername(postgreSQLContainer.getUsername());
|
||||
driverManagerDataSource.setPassword(postgreSQLContainer.getPassword());
|
||||
dataSource = driverManagerDataSource;
|
||||
jobExplorer = jobExplorer();
|
||||
taskExplorer = taskExplorer();
|
||||
}
|
||||
|
||||
private static JobExplorer jobExplorer() throws Exception {
|
||||
JobExplorerFactoryBean factoryBean = new JobExplorerFactoryBean();
|
||||
factoryBean.setDataSource(dataSource);
|
||||
factoryBean.setSerializer(new Jackson2ExecutionContextStringSerializer());
|
||||
factoryBean.setJdbcOperations(new JdbcTemplate(dataSource));
|
||||
return factoryBean.getObject();
|
||||
}
|
||||
|
||||
private static TaskExplorer taskExplorer() {
|
||||
TaskExecutionDaoFactoryBean taskExecutionDaoFactoryBean = new TaskExecutionDaoFactoryBean(dataSource, "TASK_");
|
||||
return new SimpleTaskExplorer(taskExecutionDaoFactoryBean);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccessfulTask() {
|
||||
String taskName = "taskSuccessfulTask";
|
||||
List<String> args = getTaskLaunchArgs(taskName);
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
args.toArray(new String[0]));
|
||||
List<TaskExecution> taskExecutions = getTaskExecutions(taskName);
|
||||
assertThat(taskExecutions.size()).isEqualTo(1);
|
||||
assertThat(taskExecutions.get(0).getExitCode()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailTask() {
|
||||
String taskName = "testFailTask";
|
||||
List<String> args = getTaskLaunchArgs(taskName);
|
||||
args.add("--io.spring.fail-task=true");
|
||||
launchAppVerifyTaskFails(args, ExpectedException.class);
|
||||
List<TaskExecution> taskExecutions = getTaskExecutions(taskName);
|
||||
assertThat(taskExecutions.size()).isEqualTo(1);
|
||||
assertThat(taskExecutions.get(0).getExitCode()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccessTaskWithFailedBatchAndRestart() {
|
||||
final String jobName = "testSuccessTaskWithFailedBatchAndRestart";
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
getFailBatchArgs(jobName).toArray(new String[0])
|
||||
);
|
||||
List<JobExecution> jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitDescription()).startsWith("io.spring.scenariotask.configuration.ExpectedException");
|
||||
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
getFailBatchArgs(jobName).toArray(new String[0]));
|
||||
jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(2);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.COMPLETED.getExitCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccessTaskSuccessBatchAndRestartFailure() throws Exception{
|
||||
final String jobName = "testSuccessTaskSuccessBatchAndRestartFailure";
|
||||
List<String> args = getSuccessBatchArgs(jobName);
|
||||
args.add("--io.spring.include-runid-incrementer=true");
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
args.toArray(new String[0])
|
||||
);
|
||||
List<JobExecution> jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.COMPLETED.getExitCode());
|
||||
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
args.toArray(new String[0]));
|
||||
assertThat(jobExplorer.getJobInstanceCount(jobName)).isEqualTo(2);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testFailTaskWithFailedBatchAndRestart() {
|
||||
final String jobName = "testFailTaskWithFailedBatchAndRestart";
|
||||
final String taskName = "testFailTaskWithFailedBatchAndRestartTask";
|
||||
List<String> args = getFailBatchArgs(jobName);
|
||||
args.add("--spring.application.name=" + taskName);
|
||||
args.add("--spring.cloud.task.batch.fail-on-job-failure=true");
|
||||
launchAppVerifyTaskFails(args, TaskException.class);
|
||||
List<JobExecution> jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode());
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitDescription()).startsWith("io.spring.scenariotask.configuration.ExpectedException");
|
||||
List<TaskExecution> taskExecutions = getTaskExecutions(taskName);
|
||||
assertThat(taskExecutions.size()).isEqualTo(1);
|
||||
assertThat(taskExecutions.get(0).getExitCode()).isEqualTo(1);
|
||||
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
args.toArray(new String[0]));
|
||||
jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(2);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.COMPLETED.getExitCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccessfulTaskBatch() {
|
||||
final String jobName = "testSuccessfulTaskBatch";
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
getSuccessBatchArgs(jobName).toArray(new String[0]));
|
||||
List<JobExecution> jobExecutions = getJobExecutionsForLastJobInstance(jobName);
|
||||
assertThat(jobExecutions.size()).isEqualTo(1);
|
||||
assertThat(jobExecutions.get(0).getExitStatus().getExitCode()).isEqualTo(ExitStatus.COMPLETED.getExitCode());
|
||||
}
|
||||
|
||||
private void launchAppVerifyTaskFails(List<String> args, Class clazz) {
|
||||
assertThatThrownBy(() -> {
|
||||
SpringApplication.run(ScenarioTaskApplication.class,
|
||||
args.toArray(new String[0]));
|
||||
}).isInstanceOf(IllegalStateException.class)
|
||||
.getCause().isInstanceOf(clazz);
|
||||
}
|
||||
|
||||
private List<JobExecution> getJobExecutionsForLastJobInstance(String jobName) {
|
||||
long instanceId = jobExplorer.getLastJobInstance(jobName).getInstanceId();
|
||||
return jobExplorer.getJobExecutions(jobExplorer.getJobInstance(instanceId));
|
||||
}
|
||||
|
||||
private List<String> getTaskLaunchArgs(String taskName) {
|
||||
List<String> args = new ArrayList<>(getDatabaseArgs());
|
||||
args.add("--io.spring.launchBatchJob=false");
|
||||
args.add("--spring.application.name=" + taskName);
|
||||
return args;
|
||||
}
|
||||
|
||||
private List<String> getDatabaseArgs() {
|
||||
List<String> args = Arrays.asList(
|
||||
"--spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
|
||||
"--spring.datasource.driverClassName=" + "org.postgresql.Driver",
|
||||
"--spring.datasource.username=" + postgreSQLContainer.getUsername(),
|
||||
"--spring.datasource.password=" + postgreSQLContainer.getPassword());
|
||||
return args;
|
||||
}
|
||||
|
||||
private List<String> getSuccessBatchArgs(String jobName) {
|
||||
List<String> args = new ArrayList<>(getDatabaseArgs());
|
||||
args.add("--io.spring.job-name=" + jobName);
|
||||
args.add("--spring.batch.initialize-schema=always");
|
||||
return args;
|
||||
}
|
||||
|
||||
private List<String> getFailBatchArgs(String jobName) {
|
||||
List<String> args = new ArrayList<>(getSuccessBatchArgs(jobName));
|
||||
args.add("--io.spring.fail-batch=true");
|
||||
return args;
|
||||
}
|
||||
|
||||
private List<TaskExecution> getTaskExecutions(String taskName) {
|
||||
Page<TaskExecution> taskExecutionPage = taskExplorer.findTaskExecutionsByName(taskName, PageRequest.of(0, 5));
|
||||
return taskExecutionPage.getContent();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user