org.trpr.platform.batch.impl.spring.admin.SimpleJobService.java Source code

Java tutorial

Introduction

Here is the source code for org.trpr.platform.batch.impl.spring.admin.SimpleJobService.java

Source

/*
 * Copyright 2012-2015, 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 org.trpr.platform.batch.impl.spring.admin;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;

import org.springframework.batch.admin.service.JobService;
import org.springframework.batch.admin.service.NoSuchStepExecutionException;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.launch.NoSuchJobExecutionException;
import org.springframework.batch.core.launch.NoSuchJobInstanceException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.step.NoSuchStepException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.annotation.Scheduled;
import org.trpr.platform.core.impl.logging.LogFactory;
import org.trpr.platform.core.spi.logging.Logger;

/**
 * The <code>SimpleJobService</code> class is an implementation of {@link JobService} that delegates most of its work to the standard Spring Batch 
 * components
 * 
 * @author Regunath B
 * @version 1.0, 19 Sep 2012
 */
public class SimpleJobService implements JobService, DisposableBean {

    /** Default shutdown timeout - 60 seconds */
    private static final int DEFAULT_SHUTDOWN_TIMEOUT = 60 * 1000;

    /** Logger instance for this class*/
    private static final Logger LOGGER = LogFactory.getLogger(SimpleJobService.class);

    /** The shutdown timeout*/
    private int shutdownTimeout = DEFAULT_SHUTDOWN_TIMEOUT;

    /** List of active JobExecutionS*/
    private Collection<JobExecution> activeExecutions = Collections.synchronizedList(new ArrayList<JobExecution>());

    /** The JobRepository component*/
    private JobRepository jobRepository;

    /** The JobRepository component*/
    private JobRegistry jobRegistry;

    /** The JobLauncher component*/
    private JobLauncher jobLauncher;

    /** The JobExplorer component*/
    private JobExplorer jobExplorer;

    /**
     * Constructor for this class
     * @param jobRepository the JobRepository
     */
    public SimpleJobService(JobRepository jobRepository, JobExplorer jobExplorer, JobRegistry jobRegistry,
            JobLauncher jobLauncher) {
        this.jobRepository = jobRepository;
        this.jobExplorer = jobExplorer;
        this.jobRegistry = jobRegistry;
        this.jobLauncher = jobLauncher;
    }

    /**
     * Interface method implementation.Stops all the active jobs and wait for them (up to a time out) to finish
     * processing.
     * @see org.springframework.beans.factory.DisposableBean#destroy()
     */
    public void destroy() throws Exception {
        Exception firstException = null;
        for (JobExecution jobExecution : activeExecutions) {
            try {
                if (jobExecution.isRunning()) {
                    stop(jobExecution.getId());
                }
            } catch (JobExecutionNotRunningException e) {
                LOGGER.info("JobExecution is not running so it cannot be stopped");
            } catch (Exception e) {
                LOGGER.error("Unexpected exception stopping JobExecution", e);
                if (firstException == null) {
                    firstException = e;
                }
            }
        }
        int count = 0;
        int maxCount = (shutdownTimeout + 1000) / 1000;
        while (!activeExecutions.isEmpty() && ++count < maxCount) {
            LOGGER.error("Waiting for " + activeExecutions.size() + " active executions to complete");
            removeInactiveExecutions();
            Thread.sleep(1000L);
        }
        if (firstException != null) {
            throw firstException;
        }
    }

    /**
     * Check all the active executions and see if they are still actually
     * running. Remove the ones that have completed.
     */
    @Scheduled(fixedDelay = 60000)
    public void removeInactiveExecutions() {
        for (Iterator<JobExecution> iterator = activeExecutions.iterator(); iterator.hasNext();) {
            JobExecution jobExecution = iterator.next();
            try {
                jobExecution = getJobExecution(jobExecution.getId());
            } catch (NoSuchJobExecutionException e) {
                LOGGER.error("Unexpected exception loading JobExecution", e);
            }
            if (!jobExecution.isRunning()) {
                iterator.remove();
            }
        }
    }

    /**
     * Interface method implementation. 
     * @see org.springframework.batch.admin.service.JobService#abandon(java.lang.Long)
     */
    public JobExecution abandon(Long jobExecutionId)
            throws NoSuchJobExecutionException, JobExecutionAlreadyRunningException {
        JobExecution jobExecution = getJobExecution(jobExecutionId);
        if (jobExecution.getStatus().isLessThan(BatchStatus.STOPPING)) {
            throw new JobExecutionAlreadyRunningException(
                    "JobExecution is running or complete and therefore cannot be aborted");
        }

        LOGGER.info("Aborting job execution: " + jobExecution);
        jobExecution.upgradeStatus(BatchStatus.ABANDONED);
        jobExecution.setEndTime(new Date());
        jobRepository.update(jobExecution);
        return jobExecution;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#countJobExecutions()
     */
    public int countJobExecutions() {
        int count = 0;
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                count += this.jobExplorer.getJobExecutions(jobInstance).size();
            }

        }
        return count;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#countJobExecutionsForJob(java.lang.String)
     */
    public int countJobExecutionsForJob(String jobName) throws NoSuchJobException {
        int count = 0;
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.equalsIgnoreCase(jobName)) {
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                    count += this.jobExplorer.getJobExecutions(jobInstance).size();
                }
                break;
            }
        }
        return count;
    }

    /**
     * Interface method implementation.
     * @see org.springframework.batch.admin.service.JobService#countJobInstances(java.lang.String)
     */
    public int countJobInstances(String jobName) throws NoSuchJobException {
        int count = 0;
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.equalsIgnoreCase(jobName)) {
                count += this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE).size();
                break;
            }
        }
        return count;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#countJobs()
     */
    public int countJobs() {
        return this.jobRegistry.getJobNames().size();
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#countStepExecutionsForStep(java.lang.String, java.lang.String)
     */
    public int countStepExecutionsForStep(String jobName, String stepName) throws NoSuchStepException {
        int count = 0;
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                    for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                        Collection<StepExecution> stepExecutions = jobExecution.getStepExecutions();
                        for (StepExecution step : stepExecutions) {
                            if (step.getStepName().contains(stepName)) {
                                count += 1;
                            }
                        }
                    }
                }
            }
        }
        return count;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getJobExecution(java.lang.Long)
     */
    public JobExecution getJobExecution(Long jobExecutionId) throws NoSuchJobExecutionException {
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                    if (jobExecution.getId() == jobExecutionId) {
                        return jobExecution;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getJobExecutionsForJobInstance(java.lang.String, java.lang.Long)
     */
    public Collection<JobExecution> getJobExecutionsForJobInstance(String jobName, Long jobInstanceId)
            throws NoSuchJobException {
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                    if (jobInstance.getId() == jobInstanceId) {
                        return this.jobExplorer.getJobExecutions(jobInstance);
                    }
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getJobInstance(long)
     */
    public JobInstance getJobInstance(long jobInstanceId) throws NoSuchJobInstanceException {
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                if (jobInstance.getId() == jobInstanceId) {
                    return jobInstance;
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getLastJobParameters(java.lang.String)
     */
    public JobParameters getLastJobParameters(String jobName) throws NoSuchJobException {
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                // get the last run JobInstance if any
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, 1)) { // end is set as 1 to get a single element List
                    return jobInstance.getJobParameters();
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getStepExecution(java.lang.Long, java.lang.Long)
     */
    public StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId)
            throws NoSuchStepExecutionException, NoSuchJobExecutionException {
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                    if (jobExecution.getId() == jobExecutionId) {
                        for (StepExecution step : jobExecution.getStepExecutions()) {
                            if (step.getId() == stepExecutionId) {
                                return step;
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getStepExecutions(java.lang.Long)
     */
    public Collection<StepExecution> getStepExecutions(Long jobExecutionId) throws NoSuchJobExecutionException {
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                    if (jobExecution.getId() == jobExecutionId) {
                        return jobExecution.getStepExecutions();
                    }
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#getStepNamesForJob(java.lang.String)
     */
    public Collection<String> getStepNamesForJob(String jobName) throws NoSuchJobException {
        Collection<String> stepNames = new LinkedHashSet<String>();
        for (JobExecution jobExecution : listJobExecutionsForJob(jobName, 0, 100)) {
            for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
                stepNames.add(stepExecution.getStepName());
            }
        }
        return Collections.unmodifiableList(new ArrayList<String>(stepNames));
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#isIncrementable(java.lang.String)
     */
    public boolean isIncrementable(String jobName) {
        try {
            return this.jobRegistry.getJobNames().contains(jobName)
                    && this.jobRegistry.getJob(jobName).getJobParametersIncrementer() != null;
        } catch (NoSuchJobException e) {
            // Should not happen
            throw new IllegalStateException("Unexpected non-existent job: " + jobName);
        }
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#isLaunchable(java.lang.String)
     */
    public boolean isLaunchable(String jobName) {
        return this.jobRegistry.getJobNames().contains(jobName);
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#launch(java.lang.String, org.springframework.batch.core.JobParameters)
     */
    public JobExecution launch(String jobName, JobParameters jobParameters)
            throws NoSuchJobException, JobExecutionAlreadyRunningException, JobRestartException,
            JobInstanceAlreadyCompleteException, JobParametersInvalidException {

        Job job = this.jobRegistry.getJob(jobName);
        JobExecution lastJobExecution = this.jobRepository.getLastJobExecution(jobName, jobParameters);
        boolean restart = false;
        if (lastJobExecution != null) {
            BatchStatus status = lastJobExecution.getStatus();
            if (status.isUnsuccessful() && status != BatchStatus.ABANDONED) {
                restart = true;
            }
        }
        if (job.getJobParametersIncrementer() != null && !restart) {
            jobParameters = job.getJobParametersIncrementer().getNext(jobParameters);
        }
        JobExecution jobExecution = this.jobLauncher.run(job, jobParameters);
        if (jobExecution.isRunning()) {
            this.activeExecutions.add(jobExecution);
        }
        return jobExecution;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#listJobExecutions(int, int)
     */
    public Collection<JobExecution> listJobExecutions(int start, int count) {
        List<JobExecution> executionList = new LinkedList<JobExecution>();
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                executionList.addAll(this.jobExplorer.getJobExecutions(jobInstance));
            }
        }
        if (start >= executionList.size()) {
            return new LinkedList<JobExecution>(); // return empty list instead of a sub-list
        }
        if (start + count >= executionList.size()) {
            count = executionList.size() - start;
        }
        return executionList.subList(start, count);
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#listJobExecutionsForJob(java.lang.String, int, int)
     */
    public Collection<JobExecution> listJobExecutionsForJob(String jobName, int start, int count)
            throws NoSuchJobException {
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                    List<JobExecution> executionList = this.jobExplorer.getJobExecutions(jobInstance);
                    if (start >= executionList.size()) {
                        return new LinkedList<JobExecution>(); // return empty list instead of a sub-list
                    }
                    if (start + count >= executionList.size()) {
                        count = executionList.size() - start;
                    }
                    return executionList.subList(start, count);
                }
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#listJobInstances(java.lang.String, int, int)
     */
    public Collection<JobInstance> listJobInstances(String jobName, int start, int count)
            throws NoSuchJobException {
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                List<JobInstance> instanceList = this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE);
                if (start >= instanceList.size()) {
                    return new LinkedList<JobInstance>(); // return empty list instead of a sub-list
                }
                if (start + count >= instanceList.size()) {
                    count = instanceList.size() - start;
                }
                return instanceList.subList(start, count);
            }
        }
        return null;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#listJobs(int, int)
     */
    public Collection<String> listJobs(int start, int count) {
        List<String> jobNames = new LinkedList<String>();
        jobNames.addAll(this.jobRegistry.getJobNames());
        if (start >= jobNames.size()) {
            return new LinkedList<String>(); // return empty list instead of a sub-list
        }
        if (start + count >= jobNames.size()) {
            count = jobNames.size() - start;
        }
        return jobNames.subList(start, count);
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#listStepExecutionsForStep(java.lang.String, java.lang.String, int, int)
     */
    public Collection<StepExecution> listStepExecutionsForStep(String jobName, String stepName, int start,
            int count) throws NoSuchStepException {
        if (this.countStepExecutionsForStep(jobName, stepName) == 0) {
            throw new NoSuchStepException("No step executions exist with this step name: " + stepName);
        }
        List<StepExecution> steps = new LinkedList<StepExecution>();
        for (String name : this.jobRegistry.getJobNames()) {
            if (name.contains(jobName)) {
                for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                    for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                        Collection<StepExecution> stepExecutions = jobExecution.getStepExecutions();
                        for (StepExecution step : stepExecutions) {
                            if (step.getStepName().contains(stepName)) {
                                steps.add(step);
                            }
                        }
                    }
                }
            }
        }
        if (start >= steps.size()) {
            return new LinkedList<StepExecution>(); // return empty list instead of a sub-list
        }
        if (start + count >= steps.size()) {
            count = steps.size() - start;
        }
        return steps.subList(start, count);
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#restart(java.lang.Long)
     */
    public JobExecution restart(Long jobExecutionId)
            throws NoSuchJobExecutionException, JobExecutionAlreadyRunningException, JobRestartException,
            JobInstanceAlreadyCompleteException, NoSuchJobException, JobParametersInvalidException {
        JobExecution target = getJobExecution(jobExecutionId);
        JobInstance lastInstance = target.getJobInstance();
        Job job = this.jobRegistry.getJob(lastInstance.getJobName());
        JobExecution jobExecution = this.jobLauncher.run(job, lastInstance.getJobParameters());
        if (jobExecution.isRunning()) {
            this.activeExecutions.add(jobExecution);
        }
        return jobExecution;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#stop(java.lang.Long)
     */
    public JobExecution stop(Long jobExecutionId)
            throws NoSuchJobExecutionException, JobExecutionNotRunningException {
        JobExecution jobExecution = getJobExecution(jobExecutionId);
        if (!jobExecution.isRunning()) {
            throw new JobExecutionNotRunningException(
                    "JobExecution is not running and therefore cannot be stopped");
        }
        LOGGER.info("Stopping job execution: " + jobExecution);
        jobExecution.stop();
        jobRepository.update(jobExecution);
        return jobExecution;
    }

    /**
     * Interface method implementation
     * @see org.springframework.batch.admin.service.JobService#stopAll()
     */
    public int stopAll() {
        List<JobExecution> allExecutions = new LinkedList<JobExecution>();
        for (String jobName : this.jobRegistry.getJobNames()) {
            for (JobInstance jobInstance : this.jobExplorer.getJobInstances(jobName, 0, Integer.MAX_VALUE)) {
                for (JobExecution jobExecution : this.jobExplorer.getJobExecutions(jobInstance)) {
                    if (jobExecution.isRunning()) {
                        allExecutions.add(jobExecution);
                    }
                }
            }
        }
        for (JobExecution jobExecution : allExecutions) {
            jobExecution.stop();
            this.jobRepository.update(jobExecution);
        }
        return allExecutions.size();
    }

    /** Getter/Setter methods*/
    /**
     * Timeout for shutdown waiting for jobs to finish processing.
     * @param shutdownTimeout in milliseconds (default 60 secs)
     */
    public void setShutdownTimeout(int shutdownTimeout) {
        this.shutdownTimeout = shutdownTimeout;
    }
    /** End getter/setter methods*/

}