Skip to content

Commit

Permalink
feature: UnexpectedTaskExecutionException for CTR
Browse files Browse the repository at this point in the history
  • Loading branch information
klopfdreh committed Nov 9, 2023
1 parent a25ad34 commit 81bdc43
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.slf4j.LoggerFactory;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
Expand All @@ -40,6 +39,7 @@
import org.springframework.cloud.dataflow.composedtaskrunner.properties.ComposedTaskProperties;
import org.springframework.cloud.dataflow.composedtaskrunner.support.ComposedTaskException;
import org.springframework.cloud.dataflow.composedtaskrunner.support.TaskExecutionTimeoutException;
import org.springframework.cloud.dataflow.composedtaskrunner.support.UnexpectedTaskExecutionException;
import org.springframework.cloud.dataflow.rest.client.DataFlowOperations;
import org.springframework.cloud.dataflow.rest.client.DataFlowTemplate;
import org.springframework.cloud.dataflow.rest.client.TaskOperations;
Expand Down Expand Up @@ -234,10 +234,12 @@ public RepeatStatus execute(StepContribution contribution, ChunkContext chunkCon
TaskExecution taskExecution = this.taskExplorer.getTaskExecution(this.executionId);
if (taskExecution != null && taskExecution.getEndTime() != null) {
if (taskExecution.getExitCode() == null) {
throw new UnexpectedJobExecutionException("Task returned a null exit code.");
} else if (taskExecution.getExitCode() != 0) {
throw new UnexpectedJobExecutionException("Task returned a non zero exit code.");
} else {
throw new UnexpectedTaskExecutionException("Task returned a null exit code.", taskExecution);
}
else if (taskExecution.getExitCode() != 0) {
throw new UnexpectedTaskExecutionException("Task returned a non zero exit code.", taskExecution);
}
else {
return RepeatStatus.FINISHED;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright 2017-2023 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
*
* https://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.dataflow.composedtaskrunner.support;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.cloud.task.repository.TaskExecution;

/**
* Creates a {@link UnexpectedTaskExecutionException} which extends {@link UnsupportedOperationException}, but
* also contains the exitCode as information.
*
* @author Tobias Soloschenko
*/
public class UnexpectedTaskExecutionException extends UnexpectedJobExecutionException implements ExitCodeGenerator {

private static final long serialVersionUID = 3494615323781500165L;

/**
* The unique id associated with the task execution.
*/
private long executionId;

/**
* The parent task execution id.
*/
private Long parentExecutionId;

/**
* The recorded exit code for the task.
*/
private Integer exitCode = -1;

/**
* User defined name for the task.
*/
private String taskName;

/**
* Time of when the task was started.
*/
private Date startTime;

/**
* Timestamp of when the task was completed/terminated.
*/
private Date endTime;

/**
* Message returned from the task or stacktrace.
*/
private String exitMessage;

/**
* Id assigned to the task by the platform.
*/
private String externalExecutionId;

/**
* Error information available upon the failure of a task.
*/
private String errorMessage;

/**
* The arguments that were used for this task execution.
*/
private ArrayList<String> arguments;

/**
* Constructs an UnexpectedTaskExecutionException with the specified
* detail message.
*
* @param message the detail message
*/
public UnexpectedTaskExecutionException(String message) {
super(message);
}

/**
* Constructs an UnexpectedTaskExecutionException with the specified
* detail message, cause and exitCode.
*
* @param message the detail message
* @param cause the cause which leads to this exception
*/
public UnexpectedTaskExecutionException(String message, Throwable cause) {
super(message, cause);
}

/**
* Constructs an UnexpectedTaskExecutionException with the specified
* detail message and taskExecution.
*
* @param message the detail message
* @param taskExecution the taskExecution of the task
*/
public UnexpectedTaskExecutionException(String message, TaskExecution taskExecution) {
this(message);
assignTaskExecutionFields(taskExecution);
}

/**
* Constructs an UnexpectedTaskExecutionException with the specified
* detail message, cause and taskExecution.
*
* @param message the detail message
* @param cause the cause which leads to this exception
* @param taskExecution the taskExecution of the task
*/
public UnexpectedTaskExecutionException(String message, Throwable cause, TaskExecution taskExecution) {
this(message, cause);
assignTaskExecutionFields(taskExecution);
}

/**
* Assigns the task execution fields to this exception.
*
* @param taskExecution the task execution of which the fields should be assigned to this exception
*/
private void assignTaskExecutionFields(TaskExecution taskExecution) {
if(taskExecution != null) {
executionId = taskExecution.getExecutionId();
parentExecutionId = taskExecution.getParentExecutionId();
exitCode = taskExecution.getExitCode();
taskName = taskExecution.getTaskName();
startTime = taskExecution.getStartTime();
endTime = taskExecution.getEndTime();
externalExecutionId = taskExecution.getExternalExecutionId();
errorMessage = taskExecution.getErrorMessage();
exitMessage = taskExecution.getExitMessage();
arguments = new ArrayList<>(taskExecution.getArguments());
}
}

public long getExecutionId() {
return this.executionId;
}

/**
* Returns the exit code of the task.
*
* @return the exit code or -1 if the exit code couldn't be determined
*/
@Override
public int getExitCode() {
return this.exitCode;
}

public String getTaskName() {
return this.taskName;
}

public Date getStartTime() {
return (this.startTime != null) ? (Date) this.startTime.clone() : null;
}

public Date getEndTime() {
return (this.endTime != null) ? (Date) this.endTime.clone() : null;
}

public String getExitMessage() {
return this.exitMessage;
}

public List<String> getArguments() {
return this.arguments;
}

public String getErrorMessage() {
return this.errorMessage;
}

public String getExternalExecutionId() {
return this.externalExecutionId;
}

public Long getParentExecutionId() {
return this.parentExecutionId;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.UnexpectedJobExecutionException;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.scope.context.StepContext;
Expand All @@ -52,6 +51,7 @@
import org.springframework.cloud.dataflow.composedtaskrunner.properties.ComposedTaskProperties;
import org.springframework.cloud.dataflow.composedtaskrunner.support.ComposedTaskException;
import org.springframework.cloud.dataflow.composedtaskrunner.support.TaskExecutionTimeoutException;
import org.springframework.cloud.dataflow.composedtaskrunner.support.UnexpectedTaskExecutionException;
import org.springframework.cloud.dataflow.core.database.support.MultiSchemaTaskExecutionDaoFactoryBean;
import org.springframework.cloud.dataflow.rest.client.DataFlowClientException;
import org.springframework.cloud.dataflow.rest.client.DataFlowOperations;
Expand Down Expand Up @@ -318,10 +318,14 @@ public void testTaskLauncherTaskletFailure() {
mockReturnValForTaskExecution(1L);
TaskLauncherTasklet taskLauncherTasklet = getTaskExecutionTasklet();
ChunkContext chunkContext = chunkContext();
createCompleteTaskExecution(1);
Throwable exception = assertThrows(UnexpectedJobExecutionException.class,
createCompleteTaskExecution(1, "This is the exit message of the task itself.");
UnexpectedTaskExecutionException exception = assertThrows(UnexpectedTaskExecutionException.class,
() -> execute(taskLauncherTasklet, null, chunkContext));
Assertions.assertThat(exception.getMessage()).isEqualTo("Task returned a non zero exit code.");
Assertions.assertThat(exception.getMessage()).isEqualTo("Task returned a non zero exit code.");
Assertions.assertThat(exception.getExitCode()).isEqualTo(1);
Assertions.assertThat(exception.getExitMessage()).isEqualTo("This is the exit message of the task itself.");
Assertions.assertThat(exception.getEndTime()).isNotNull();
}

private RepeatStatus execute(TaskLauncherTasklet taskLauncherTasklet, StepContribution contribution,
Expand All @@ -341,7 +345,7 @@ public void testTaskLauncherTaskletNullResult() {
TaskLauncherTasklet taskLauncherTasklet = getTaskExecutionTasklet();
ChunkContext chunkContext = chunkContext();
getCompleteTaskExecutionWithNull();
Throwable exception = assertThrows(UnexpectedJobExecutionException.class,
Throwable exception = assertThrows(UnexpectedTaskExecutionException.class,
() -> execute(taskLauncherTasklet, null, chunkContext));
Assertions.assertThat(exception.getMessage()).isEqualTo("Task returned a null exit code.");
}
Expand Down Expand Up @@ -455,10 +459,10 @@ public void testTaskOperationsConfiguredWithMissingUsername() {
}
fail("Expected an IllegalArgumentException to be thrown");
}
private void createCompleteTaskExecution(int exitCode) {
private void createCompleteTaskExecution(int exitCode, String... message) {
TaskExecution taskExecution = this.taskRepository.createTaskExecution();
this.taskRepository.completeTaskExecution(taskExecution.getExecutionId(),
exitCode, new Date(), "");
exitCode, new Date(), message != null && message.length > 0 ? message[0] : "");
}

private void createAndStartCompleteTaskExecution(int exitCode, JobExecution jobExecution) {
Expand Down

0 comments on commit 81bdc43

Please sign in to comment.