/**
* Copyright (c) 2010 Yahoo! Inc. All rights reserved.
* 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. See accompanying LICENSE file.
*/
package org.apache.oozie.service;
import java.util.Date;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.oozie.BundleJobBean;
import org.apache.oozie.CoordinatorJobBean;
import org.apache.oozie.command.bundle.BundlePauseXCommand;
import org.apache.oozie.command.bundle.BundleStartXCommand;
import org.apache.oozie.command.bundle.BundleUnpauseXCommand;
import org.apache.oozie.command.coord.CoordPauseXCommand;
import org.apache.oozie.command.coord.CoordUnpauseXCommand;
import org.apache.oozie.executor.jpa.BundleJobsGetNeedStartJPAExecutor;
import org.apache.oozie.executor.jpa.BundleJobsGetPausedJPAExecutor;
import org.apache.oozie.executor.jpa.BundleJobsGetUnpausedJPAExecutor;
import org.apache.oozie.executor.jpa.CoordJobsGetPausedJPAExecutor;
import org.apache.oozie.executor.jpa.CoordJobsGetUnpausedJPAExecutor;
import org.apache.oozie.service.SchedulerService;
import org.apache.oozie.service.Service;
import org.apache.oozie.service.Services;
import org.apache.oozie.util.MemoryLocks;
import org.apache.oozie.util.XLog;
/**
* PauseTransitService is the runnable which is scheduled to run at the configured interval, it checks all bundles
* to see if they should be paused, un-paused or started.
*/
public class PauseTransitService implements Service {
public static final String CONF_PREFIX = Service.CONF_PREFIX + "PauseTransitService.";
public static final String CONF_BUNDLE_PAUSE_START_INTERVAL = CONF_PREFIX + "PauseTransit.interval";
private final static XLog LOG = XLog.getLog(PauseTransitService.class);
/**
* PauseTransitRunnable is the runnable which is scheduled to run at the configured interval, it checks all
* bundles to see if they should be paused, un-paused or started.
*/
static class PauseTransitRunnable implements Runnable {
private JPAService jpaService = null;
private MemoryLocks.LockToken lock;
public PauseTransitRunnable() {
jpaService = Services.get().get(JPAService.class);
if (jpaService == null) {
LOG.error("Missing JPAService");
}
}
public void run() {
try {
// first check if there is some other running instance from the same service;
lock = Services.get().get(MemoryLocksService.class).getWriteLock(
PauseTransitService.class.getName(), lockTimeout);
if (lock == null) {
LOG.info("This PauseTransitService instance will"
+ "not run since there is already an instance running");
}
else {
LOG.info("Acquired lock for [{0}]", PauseTransitService.class.getName());
updateBundle();
updateCoord();
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing/starting bundle/coord jobs", ex);
}
finally {
// release lock;
if (lock != null) {
lock.release();
LOG.info("Released lock for [{0}]", PauseTransitService.class.getName());
}
}
}
private void updateBundle() {
Date d = new Date(); // records the start time of this service run;
List<BundleJobBean> jobList = null;
// pause bundles as needed;
try {
jobList = jpaService.execute(new BundleJobsGetUnpausedJPAExecutor(-1));
if (jobList != null) {
for (BundleJobBean bundleJob : jobList) {
if ((bundleJob.getPauseTime() != null) && !bundleJob.getPauseTime().after(d)) {
new BundlePauseXCommand(bundleJob).call();
LOG.debug("Calling BundlePauseXCommand for bundle job = " + bundleJob.getId());
}
}
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
}
// unpause bundles as needed;
try {
jobList = jpaService.execute(new BundleJobsGetPausedJPAExecutor(-1));
if (jobList != null) {
for (BundleJobBean bundleJob : jobList) {
if ((bundleJob.getPauseTime() == null || bundleJob.getPauseTime().after(d))) {
new BundleUnpauseXCommand(bundleJob).call();
LOG.debug("Calling BundleUnpauseXCommand for bundle job = " + bundleJob.getId());
}
}
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
}
// start bundles as needed;
try {
jobList = jpaService.execute(new BundleJobsGetNeedStartJPAExecutor(d));
if (jobList != null) {
for (BundleJobBean bundleJob : jobList) {
bundleJob.setKickoffTime(d);
new BundleStartXCommand(bundleJob.getId()).call();
LOG.debug("Calling BundleStartXCommand for bundle job = " + bundleJob.getId());
}
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
}
}
private void updateCoord() {
Date d = new Date(); // records the start time of this service run;
List<CoordinatorJobBean> jobList = null;
Configuration conf = Services.get().getConf();
boolean backwardSupportForCoordStatus = conf.getBoolean(StatusTransitService.CONF_BACKWARD_SUPPORT_FOR_COORD_STATUS, false);
// pause coordinators as needed;
try {
jobList = jpaService.execute(new CoordJobsGetUnpausedJPAExecutor(-1));
if (jobList != null) {
for (CoordinatorJobBean coordJob : jobList) {
// if namespace 0.1 is used and backward support is true, then ignore this coord job
if (backwardSupportForCoordStatus == true && coordJob.getAppNamespace() != null
&& coordJob.getAppNamespace().equals(SchemaService.COORDINATOR_NAMESPACE_URI_1)) {
continue;
}
if ((coordJob.getPauseTime() != null) && !coordJob.getPauseTime().after(d)) {
new CoordPauseXCommand(coordJob).call();
LOG.debug("Calling CoordPauseXCommand for coordinator job = " + coordJob.getId());
}
}
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing Coordinator jobs", ex);
}
// unpause coordinators as needed;
try {
jobList = jpaService.execute(new CoordJobsGetPausedJPAExecutor(-1));
if (jobList != null) {
for (CoordinatorJobBean coordJob : jobList) {
// if namespace 0.1 is used and backward support is true, then ignore this coord job
if (backwardSupportForCoordStatus == true && coordJob.getAppNamespace() != null
&& coordJob.getAppNamespace().equals(SchemaService.COORDINATOR_NAMESPACE_URI_1)) {
continue;
}
if ((coordJob.getPauseTime() == null || coordJob.getPauseTime().after(d))) {
new CoordUnpauseXCommand(coordJob).call();
LOG.debug("Calling CoordUnpauseXCommand for coordinator job = " + coordJob.getId());
}
}
}
}
catch (Exception ex) {
LOG.warn("Exception happened when pausing/unpausing Coordinator jobs", ex);
}
}
}
/**
* Initializes the {@link PauseTransitService}.
*
* @param services services instance.
*/
@Override
public void init(Services services) {
Configuration conf = services.getConf();
Runnable bundlePauseStartRunnable = new PauseTransitRunnable();
services.get(SchedulerService.class).schedule(bundlePauseStartRunnable, 10,
conf.getInt(CONF_BUNDLE_PAUSE_START_INTERVAL, 60), SchedulerService.Unit.SEC);
}
/**
* Destroy the StateTransit Jobs Service.
*/
@Override
public void destroy() {
}
/**
* Return the public interface for the purge jobs service.
*
* @return {@link PauseTransitService}.
*/
@Override
public Class<? extends Service> getInterface() {
return PauseTransitService.class;
}
}