org.jumpmind.symmetric.job.AbstractJob.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.symmetric.job.AbstractJob.java

Source

/**
 * Licensed to JumpMind Inc under one or more contributor
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU General Public License, version 3.0 (GPLv3)
 * (the "License"); you may not use this file except in compliance
 * with the License.
 *
 * You should have received a copy of the GNU General Public License,
 * version 3.0 (GPLv3) along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 *
 * 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.jumpmind.symmetric.job;

import java.util.Date;
import java.util.concurrent.ScheduledFuture;

import org.apache.commons.lang.StringUtils;
import org.jumpmind.symmetric.ISymmetricEngine;
import org.jumpmind.symmetric.common.Constants;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.model.Lock;
import org.jumpmind.symmetric.service.IParameterService;
import org.jumpmind.util.RandomTimeSlot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedMetric;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

@ManagedResource(description = "The management interface for a job")
abstract public class AbstractJob implements Runnable, IJob {

    protected final Logger log = LoggerFactory.getLogger(getClass());

    private String jobName;

    private boolean requiresRegistration = true;

    private boolean paused = false;

    private Date lastFinishTime;

    private boolean running = false;

    private long lastExecutionTimeInMs;

    private long totalExecutionTimeInMs;

    private long numberOfRuns;

    private boolean started;

    private boolean hasNotRegisteredMessageBeenLogged = false;

    private ThreadPoolTaskScheduler taskScheduler;

    private ScheduledFuture<?> scheduledJob;

    private RandomTimeSlot randomTimeSlot;

    private boolean autoStartConfigured;

    protected ISymmetricEngine engine;

    protected AbstractJob(String jobName, boolean requiresRegistration, boolean autoStartRequired,
            ISymmetricEngine engine, ThreadPoolTaskScheduler taskScheduler) {
        this.engine = engine;
        this.taskScheduler = taskScheduler;
        this.jobName = jobName;
        this.requiresRegistration = requiresRegistration;
        this.autoStartConfigured = autoStartRequired;
        IParameterService parameterService = engine.getParameterService();
        this.randomTimeSlot = new RandomTimeSlot(parameterService.getExternalId(),
                parameterService.getInt(ParameterConstants.JOB_RANDOM_MAX_START_TIME_MS));
    }

    public boolean isAutoStartConfigured() {
        return autoStartConfigured;
    }

    public void start() {
        if (this.scheduledJob == null && engine != null
                && !engine.getClusterService().isInfiniteLocked(getClusterLockName())) {
            String cronExpression = engine.getParameterService().getString(jobName + ".cron", null);
            int timeBetweenRunsInMs = engine.getParameterService().getInt(jobName + ".period.time.ms", -1);
            if (!StringUtils.isBlank(cronExpression)) {
                log.info("Starting {} with cron expression: {}", jobName, cronExpression);
                this.scheduledJob = taskScheduler.schedule(this, new CronTrigger(cronExpression));
                started = true;
            } else {
                int startDelay = randomTimeSlot.getRandomValueSeededByExternalId();
                long currentTimeMillis = System.currentTimeMillis();
                long lastRunTime = currentTimeMillis - timeBetweenRunsInMs;
                Lock lock = engine.getClusterService().findLocks().get(getClusterLockName());
                if (lock != null && lock.getLastLockTime() != null) {
                    long newRunTime = lock.getLastLockTime().getTime();
                    if (lastRunTime < newRunTime) {
                        lastRunTime = newRunTime;
                    }
                }
                Date firstRun = new Date(lastRunTime + timeBetweenRunsInMs + startDelay);
                log.info("Starting {} on periodic schedule: every {}ms with the first run at {}",
                        new Object[] { jobName, timeBetweenRunsInMs, firstRun });
                if (timeBetweenRunsInMs > 0) {
                    this.scheduledJob = taskScheduler.scheduleWithFixedDelay(this, firstRun, timeBetweenRunsInMs);
                    started = true;
                } else {
                    log.error("Failed to schedule this job, {}", jobName);
                }
            }
        }
    }

    public boolean stop() {
        boolean success = false;
        if (this.scheduledJob != null) {
            success = this.scheduledJob.cancel(true);
            this.scheduledJob = null;
            if (success) {
                log.info("The {} job has been cancelled", jobName);
                started = false;
            } else {
                log.warn("Failed to cancel this job, {}", jobName);
            }
        }
        return success;
    }

    public String getName() {
        return jobName;
    }

    @ManagedOperation(description = "Run this job is it isn't already running")
    public boolean invoke() {
        return invoke(true);
    }

    public boolean invoke(boolean force) {
        IParameterService parameterService = engine.getParameterService();
        boolean ran = false;
        try {
            if (engine == null) {
                log.info("Could not find a reference to the SymmetricEngine from {}", jobName);
            } else {
                if (!Thread.interrupted()) {
                    MDC.put("engineName", engine.getEngineName());
                    if (engine.isStarted()) {
                        if (!paused || force) {
                            if (!running) {
                                running = true;
                                synchronized (this) {
                                    ran = true;
                                    long startTime = System.currentTimeMillis();
                                    try {
                                        if (!requiresRegistration || (requiresRegistration
                                                && engine.getRegistrationService().isRegisteredWithServer())) {
                                            hasNotRegisteredMessageBeenLogged = false;
                                            if (parameterService.is(ParameterConstants.SYNCHRONIZE_ALL_JOBS)) {
                                                synchronized (AbstractJob.class) {
                                                    doJob(force);
                                                }
                                            } else {
                                                doJob(force);
                                            }
                                        } else {
                                            if (!hasNotRegisteredMessageBeenLogged) {
                                                log.info(
                                                        "Did not run the {} job because the engine is not registered.",
                                                        getName());
                                                hasNotRegisteredMessageBeenLogged = true;
                                            }
                                        }
                                    } finally {
                                        lastFinishTime = new Date();
                                        long endTime = System.currentTimeMillis();
                                        lastExecutionTimeInMs = endTime - startTime;
                                        totalExecutionTimeInMs += lastExecutionTimeInMs;
                                        if (lastExecutionTimeInMs > Constants.LONG_OPERATION_THRESHOLD) {
                                            engine.getStatisticManager().addJobStats(jobName, startTime, endTime,
                                                    0);
                                        }
                                        numberOfRuns++;
                                        running = false;
                                    }
                                }
                            }
                        }
                    } else {
                        log.info("The engine is not currently started.");
                    }
                } else {
                    log.warn(
                            "This thread was interrupted.  Not executing the job until the interrupted status has cleared");
                }
            }
        } catch (final Throwable ex) {
            log.error("", ex);
        }

        return ran;
    }

    /*
     * This method is called from the job
     */
    public void run() {
        MDC.put("engineName", engine != null ? engine.getEngineName() : "unknown");
        invoke(false);
    }

    abstract void doJob(boolean force) throws Exception;

    @ManagedOperation(description = "Pause this job")
    public void pause() {
        setPaused(true);
    }

    @ManagedOperation(description = "Resume the job")
    public void unpause() {
        setPaused(false);
    }

    public void setPaused(boolean paused) {
        this.paused = paused;
    }

    @ManagedAttribute(description = "If true, this job has been paused")
    public boolean isPaused() {
        return paused;
    }

    @ManagedAttribute(description = "If true, this job has been started")
    public boolean isStarted() {
        return started;
    }

    @ManagedMetric(description = "The amount of time this job spent in execution during it's last run")
    public long getLastExecutionTimeInMs() {
        return lastExecutionTimeInMs;
    }

    @ManagedAttribute(description = "The last time this job completed execution")
    public Date getLastFinishTime() {
        return lastFinishTime;
    }

    @ManagedAttribute(description = "If true, the job is already running")
    public boolean isRunning() {
        return running;
    }

    @ManagedMetric(description = "The number of times this job has been run during the lifetime of the JVM")
    public long getNumberOfRuns() {
        return numberOfRuns;
    }

    @ManagedMetric(description = "The total amount of time this job has spent in execution during the lifetime of the JVM")
    public long getTotalExecutionTimeInMs() {
        return totalExecutionTimeInMs;
    }

    @ManagedMetric(description = "The total amount of time this job has spend in execution during the lifetime of the JVM")
    public long getAverageExecutionTimeInMs() {
        if (numberOfRuns > 0) {
            return totalExecutionTimeInMs / numberOfRuns;
        } else {
            return 0;
        }
    }

    @ManagedAttribute(description = "If set, this is the cron expression that governs when the job will run")
    public String getCronExpression() {
        return engine.getParameterService().getString(jobName + ".cron", null);
    }

    @ManagedAttribute(description = "If the cron expression isn't set.  This is the amount of time that will pass before the periodic job runs again.")
    public long getTimeBetweenRunsInMs() {
        return engine.getParameterService().getInt(jobName + ".period.time.ms", -1);
    }

}