/* (c) 2017 Open Source Geospatial Foundation - all rights reserved * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.backuprestore.tasklet; import org.geoserver.backuprestore.Backup; import org.geoserver.config.util.XStreamPersisterFactory; import org.geoserver.platform.GeoServerExtensions; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.repeat.RepeatStatus; import java.util.ArrayList; import java.util.List; /** * Extension point for generic backup / restore jobs. */ public final class GenericTasklet extends AbstractCatalogBackupRestoreTasklet { // key used to register in the job context the handlers that need to run again private static final String GENERIC_CONTINUABLE_HANDLERS_KEY = "GENERIC_CONTINUABLE_HANDLERS"; public GenericTasklet(Backup backupFacade, XStreamPersisterFactory xStreamPersisterFactory) { super(backupFacade, xStreamPersisterFactory); } @Override protected void initialize(StepExecution stepExecution) { // this is invoked for each run, which means that this may be invoked multiple times for continuable handlers getAllHandlers().forEach(handler -> handler.initialize(stepExecution, this)); } @Override public RepeatStatus doExecute(StepContribution contribution, ChunkContext chunkContext, JobExecution jobExecution) throws Exception { // get the available generic handlers or the continuable ones List<GenericTaskletHandler> handlers = getHandlers(jobExecution); List<GenericTaskletHandler> continuable = new ArrayList<>(); // execute each handler and store the continuable ones handlers.forEach(handler -> { RepeatStatus status = handler.handle(contribution, chunkContext, jobExecution, this); if (status == RepeatStatus.CONTINUABLE) { continuable.add(handler); } }); // register the continuable ones overriding the existing ones putContinuableHandlers(jobExecution, continuable); if (continuable.isEmpty()) { // no continuable jobs, we are done return RepeatStatus.FINISHED; } // there is continuable jobs return RepeatStatus.CONTINUABLE; } /** * Put the provided continuable jobs in the job execution context overriding any existing ones. */ private void putContinuableHandlers(JobExecution jobExecution, List<GenericTaskletHandler> handlers) { jobExecution.getExecutionContext() .put(GENERIC_CONTINUABLE_HANDLERS_KEY, handlers); } /** * Helper method that return the handlers that should be executed. If there is any pending * continuable handler we only run the pending continuable handlers otherwise we run all * the available handlers. */ @SuppressWarnings("unchecked") private List<GenericTaskletHandler> getHandlers(JobExecution jobExecution) { // let's see if we have any pending continuable jobs Object value = jobExecution.getExecutionContext() .get(GENERIC_CONTINUABLE_HANDLERS_KEY); if (value == null || !List.class.isAssignableFrom(value.getClass())) { // no pending continuable handlers, use the normal handlers return getAllHandlers(); } List values = (List) value; if (values.isEmpty() || !GenericTaskletHandler.class.isAssignableFrom(values.get(0).getClass())) { // not what we expect, use the normal handlers return getAllHandlers(); } // pending continuable handlers return (List<GenericTaskletHandler>) values; } /** * Helper method that just retrieves all the available generic handlers contributed by extensions. */ private List<GenericTaskletHandler> getAllHandlers() { return GeoServerExtensions.extensions(GenericTaskletHandler.class); } }