Skip to content

Add afterJobSaved method and @AfterJobSaved interface (#4851) #4853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,14 @@ default void beforeJob(JobExecution jobExecution) {
default void afterJob(JobExecution jobExecution) {
}

/**
* Callback after completion of a job and job metadata is saved.
* Called after both successful and failed executions.
* In contrast to {@link JobExecutionListener#afterJob(JobExecution)},
* it is not possible to change the given jobExecution as it was already saved.
* @param jobExecution the current {@link JobExecution}
*/
default void afterJobSaved(JobExecution jobExecution) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2006-2022 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.batch.core.annotation;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks a method to be called after a {@link Job} has completed and the job metadata is saved.
* Annotated methods are called regardless of the status of the {@link JobExecution}. <br>
* <br>
* Expected signature: void afterJobSaved({@link JobExecution} jobExecution)
*
* @see JobExecutionListener
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface AfterJobSaved {

}
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ public final void execute(JobExecution execution) {
}

jobRepository.update(execution);

try {
listener.afterJobSaved(execution);
}
catch (Exception e) {
logger.error("Exception encountered in afterJobSaved callback", e);
}
}
finally {
JobSynchronizationManager.release();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.springframework.batch.core.JobParametersIncrementer;
import org.springframework.batch.core.JobParametersValidator;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.AfterJobSaved;
import org.springframework.batch.core.annotation.BeforeJob;
import org.springframework.batch.core.job.AbstractJob;
import org.springframework.batch.core.listener.JobListenerFactoryBean;
Expand Down Expand Up @@ -145,6 +146,7 @@ public B listener(Object listener) {
Set<Method> jobExecutionListenerMethods = new HashSet<>();
jobExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), BeforeJob.class));
jobExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterJob.class));
jobExecutionListenerMethods.addAll(ReflectionUtils.findMethod(listener.getClass(), AfterJobSaved.class));

if (jobExecutionListenerMethods.size() > 0) {
JobListenerFactoryBean factory = new JobListenerFactoryBean();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,17 @@ public void beforeJob(JobExecution jobExecution) {
}
}

/**
* Call the registered listeners in reverse order, respecting and prioritising those
* that implement {@link Ordered}.
* @see org.springframework.batch.core.JobExecutionListener#afterJobSaved(org.springframework.batch.core.JobExecution)
*/
@Override
public void afterJobSaved(JobExecution jobExecution) {
for (Iterator<JobExecutionListener> iterator = listeners.reverse(); iterator.hasNext();) {
JobExecutionListener listener = iterator.next();
listener.afterJobSaved(jobExecution);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.AfterJobSaved;
import org.springframework.batch.core.annotation.BeforeJob;
import org.springframework.lang.Nullable;

Expand All @@ -37,7 +38,8 @@
public enum JobListenerMetaData implements ListenerMetaData {

BEFORE_JOB("beforeJob", "before-job-method", BeforeJob.class),
AFTER_JOB("afterJob", "after-job-method", AfterJob.class);
AFTER_JOB("afterJob", "after-job-method", AfterJob.class),
AFTER_JOB_SAVED("afterJobSaved", "after-job-saved-method", AfterJobSaved .class);

private final String methodName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.AfterJobSaved;
import org.springframework.batch.core.annotation.BeforeJob;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -38,6 +39,8 @@ public class JobExecutionListenerParserTests {

public static boolean afterCalled = false;

public static boolean afterSavedCalled = false;

@Autowired
Job job;

Expand All @@ -51,6 +54,7 @@ void testListeners() throws Exception {
job.execute(jobExecution);
assertTrue(beforeCalled);
assertTrue(afterCalled);
assertTrue(afterSavedCalled);
}

public static class TestComponent {
Expand All @@ -65,6 +69,11 @@ public void after() {
afterCalled = true;
}

@AfterJobSaved
public void afterJobSaved() {
afterSavedCalled = true;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.annotation.AfterJob;
import org.springframework.batch.core.annotation.AfterJobSaved;
import org.springframework.batch.core.annotation.BeforeJob;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.launch.JobLauncher;
Expand Down Expand Up @@ -60,9 +61,10 @@ void testListeners() throws Exception {
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
assertEquals(1, AnnotationBasedJobExecutionListener.beforeJobCount);
assertEquals(1, AnnotationBasedJobExecutionListener.afterJobCount);
assertEquals(1, AnnotationBasedJobExecutionListener.afterJobSavedCount);
assertEquals(1, InterfaceBasedJobExecutionListener.beforeJobCount);
assertEquals(1, InterfaceBasedJobExecutionListener.afterJobCount);

assertEquals(1, InterfaceBasedJobExecutionListener.afterJobSavedCount);
}

@Configuration
Expand Down Expand Up @@ -100,6 +102,8 @@ static class InterfaceBasedJobExecutionListener implements JobExecutionListener

public static int afterJobCount = 0;

public static int afterJobSavedCount = 0;

@Override
public void beforeJob(JobExecution jobExecution) {
beforeJobCount++;
Expand All @@ -110,6 +114,10 @@ public void afterJob(JobExecution jobExecution) {
afterJobCount++;
}

@Override
public void afterJobSaved(JobExecution jobExecution) {
afterJobSavedCount++;
}
}

static class AnnotationBasedJobExecutionListener {
Expand All @@ -118,6 +126,8 @@ static class AnnotationBasedJobExecutionListener {

public static int afterJobCount = 0;

public static int afterJobSavedCount = 0;

@BeforeJob
public void beforeJob(JobExecution jobExecution) {
beforeJobCount++;
Expand All @@ -128,6 +138,11 @@ public void afterJob(JobExecution jobExecution) {
afterJobCount++;
}

@AfterJobSaved
public void afterJobSaved() {
afterJobSavedCount++;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,16 @@ public void beforeJob(JobExecution stepExecution) {
assertEquals(1, list.size());
}

@Test
void testAfterJobSaved() {
listener.register(new JobExecutionListener() {
@Override
public void afterJobSaved(JobExecution jobExecution) {
list.add("foo");
}
});
listener.afterJobSaved(new JobExecution(new JobInstance(11L, "testOpenJob"), null));
assertEquals(1, list.size());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ void testWithInterface() {
JobExecution jobExecution = new JobExecution(11L);
listener.beforeJob(jobExecution);
listener.afterJob(jobExecution);
listener.afterJobSaved(jobExecution);
assertTrue(delegate.beforeJobCalled);
assertTrue(delegate.afterJobCalled);
assertTrue(delegate.afterJobSavedCalled);
}

@Test
Expand Down Expand Up @@ -240,6 +242,8 @@ private static class JobListenerWithInterface implements JobExecutionListener {

boolean afterJobCalled = false;

boolean afterJobSavedCalled = false;

@Override
public void afterJob(JobExecution jobExecution) {
afterJobCalled = true;
Expand All @@ -250,6 +254,11 @@ public void beforeJob(JobExecution jobExecution) {
beforeJobCalled = true;
}

@Override
public void afterJobSaved(JobExecution jobExecution) {
afterJobSavedCalled = true;
}

}

private static class AnnotatedTestClass {
Expand Down