001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.service;
016    
017    import java.util.Date;
018    import java.util.List;
019    
020    import org.apache.hadoop.conf.Configuration;
021    import org.apache.oozie.BundleJobBean;
022    import org.apache.oozie.CoordinatorJobBean;
023    import org.apache.oozie.command.bundle.BundlePauseXCommand;
024    import org.apache.oozie.command.bundle.BundleStartXCommand;
025    import org.apache.oozie.command.bundle.BundleUnpauseXCommand;
026    import org.apache.oozie.command.coord.CoordPauseXCommand;
027    import org.apache.oozie.command.coord.CoordUnpauseXCommand;
028    import org.apache.oozie.executor.jpa.BundleJobsGetNeedStartJPAExecutor;
029    import org.apache.oozie.executor.jpa.BundleJobsGetPausedJPAExecutor;
030    import org.apache.oozie.executor.jpa.BundleJobsGetUnpausedJPAExecutor;
031    import org.apache.oozie.executor.jpa.CoordJobsGetPausedJPAExecutor;
032    import org.apache.oozie.executor.jpa.CoordJobsGetUnpausedJPAExecutor;
033    import org.apache.oozie.service.SchedulerService;
034    import org.apache.oozie.service.Service;
035    import org.apache.oozie.service.Services;
036    import org.apache.oozie.util.MemoryLocks;
037    import org.apache.oozie.util.XLog;
038    
039    /**
040     * PauseTransitService is the runnable which is scheduled to run at the configured interval, it checks all bundles
041     * to see if they should be paused, un-paused or started.
042     */
043    public class PauseTransitService implements Service {
044        public static final String CONF_PREFIX = Service.CONF_PREFIX + "PauseTransitService.";
045        public static final String CONF_BUNDLE_PAUSE_START_INTERVAL = CONF_PREFIX + "PauseTransit.interval";
046        private final static XLog LOG = XLog.getLog(PauseTransitService.class);
047    
048        /**
049         * PauseTransitRunnable is the runnable which is scheduled to run at the configured interval, it checks all
050         * bundles to see if they should be paused, un-paused or started.
051         */
052        static class PauseTransitRunnable implements Runnable {
053            private JPAService jpaService = null;
054            private MemoryLocks.LockToken lock;
055    
056            public PauseTransitRunnable() {
057                jpaService = Services.get().get(JPAService.class);
058                if (jpaService == null) {
059                    LOG.error("Missing JPAService");
060                }
061            }
062    
063            public void run() {
064                try {
065                    // first check if there is some other running instance from the same service;
066                    lock = Services.get().get(MemoryLocksService.class).getWriteLock(
067                            PauseTransitService.class.getName(), lockTimeout);
068                    if (lock == null) {
069                        LOG.info("This PauseTransitService instance will"
070                                + "not run since there is already an instance running");
071                    }
072                    else {
073                        LOG.info("Acquired lock for [{0}]", PauseTransitService.class.getName());
074    
075                        updateBundle();
076                        updateCoord();
077                    }
078                }
079                catch (Exception ex) {
080                    LOG.warn("Exception happened when pausing/unpausing/starting bundle/coord jobs", ex);
081                }
082                finally {
083                    // release lock;
084                    if (lock != null) {
085                        lock.release();
086                        LOG.info("Released lock for [{0}]", PauseTransitService.class.getName());
087                    }
088                }
089            }
090    
091            private void updateBundle() {
092                Date d = new Date(); // records the start time of this service run;
093                List<BundleJobBean> jobList = null;
094                // pause bundles as needed;
095                try {
096                    jobList = jpaService.execute(new BundleJobsGetUnpausedJPAExecutor(-1));
097                    if (jobList != null) {
098                        for (BundleJobBean bundleJob : jobList) {
099                            if ((bundleJob.getPauseTime() != null) && !bundleJob.getPauseTime().after(d)) {
100                                new BundlePauseXCommand(bundleJob).call();
101                                LOG.debug("Calling BundlePauseXCommand for bundle job = " + bundleJob.getId());
102                            }
103                        }
104                    }
105                }
106                catch (Exception ex) {
107                    LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
108                }
109                // unpause bundles as needed;
110                try {
111                    jobList = jpaService.execute(new BundleJobsGetPausedJPAExecutor(-1));
112                    if (jobList != null) {
113                        for (BundleJobBean bundleJob : jobList) {
114                            if ((bundleJob.getPauseTime() == null || bundleJob.getPauseTime().after(d))) {
115                                new BundleUnpauseXCommand(bundleJob).call();
116                                LOG.debug("Calling BundleUnpauseXCommand for bundle job = " + bundleJob.getId());
117                            }
118                        }
119                    }
120                }
121                catch (Exception ex) {
122                    LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
123                }
124                // start bundles as needed;
125                try {
126                    jobList = jpaService.execute(new BundleJobsGetNeedStartJPAExecutor(d));
127                    if (jobList != null) {
128                        for (BundleJobBean bundleJob : jobList) {
129                            bundleJob.setKickoffTime(d);
130                            new BundleStartXCommand(bundleJob.getId()).call();
131                            LOG.debug("Calling BundleStartXCommand for bundle job = " + bundleJob.getId());
132                        }
133                    }
134                }
135                catch (Exception ex) {
136                    LOG.warn("Exception happened when pausing/unpausing/starting Bundle jobs", ex);
137                }
138            }
139    
140            private void updateCoord() {
141                Date d = new Date(); // records the start time of this service run;
142                List<CoordinatorJobBean> jobList = null;
143                Configuration conf = Services.get().getConf();
144                boolean backwardSupportForCoordStatus = conf.getBoolean(StatusTransitService.CONF_BACKWARD_SUPPORT_FOR_COORD_STATUS, false);
145    
146                // pause coordinators as needed;
147                try {
148                    jobList = jpaService.execute(new CoordJobsGetUnpausedJPAExecutor(-1));
149                    if (jobList != null) {
150                        for (CoordinatorJobBean coordJob : jobList) {
151                            // if namespace 0.1 is used and backward support is true, then ignore this coord job
152                            if (backwardSupportForCoordStatus == true && coordJob.getAppNamespace() != null
153                                    && coordJob.getAppNamespace().equals(SchemaService.COORDINATOR_NAMESPACE_URI_1)) {
154                                continue;
155                            }
156                            if ((coordJob.getPauseTime() != null) && !coordJob.getPauseTime().after(d)) {
157                                new CoordPauseXCommand(coordJob).call();
158                                LOG.debug("Calling CoordPauseXCommand for coordinator job = " + coordJob.getId());
159                            }
160                        }
161                    }
162                }
163                catch (Exception ex) {
164                    LOG.warn("Exception happened when pausing/unpausing Coordinator jobs", ex);
165                }
166                // unpause coordinators as needed;
167                try {
168                    jobList = jpaService.execute(new CoordJobsGetPausedJPAExecutor(-1));
169                    if (jobList != null) {
170                        for (CoordinatorJobBean coordJob : jobList) {
171                            // if namespace 0.1 is used and backward support is true, then ignore this coord job
172                            if (backwardSupportForCoordStatus == true && coordJob.getAppNamespace() != null
173                                    && coordJob.getAppNamespace().equals(SchemaService.COORDINATOR_NAMESPACE_URI_1)) {
174                                continue;
175                            }
176                            if ((coordJob.getPauseTime() == null || coordJob.getPauseTime().after(d))) {
177                                new CoordUnpauseXCommand(coordJob).call();
178                                LOG.debug("Calling CoordUnpauseXCommand for coordinator job = " + coordJob.getId());
179                            }
180                        }
181                    }
182                }
183                catch (Exception ex) {
184                    LOG.warn("Exception happened when pausing/unpausing Coordinator jobs", ex);
185                }
186            }
187        }
188    
189        /**
190         * Initializes the {@link PauseTransitService}.
191         *
192         * @param services services instance.
193         */
194        @Override
195        public void init(Services services) {
196            Configuration conf = services.getConf();
197            Runnable bundlePauseStartRunnable = new PauseTransitRunnable();
198            services.get(SchedulerService.class).schedule(bundlePauseStartRunnable, 10,
199                    conf.getInt(CONF_BUNDLE_PAUSE_START_INTERVAL, 60), SchedulerService.Unit.SEC);
200        }
201    
202        /**
203         * Destroy the StateTransit Jobs Service.
204         */
205        @Override
206        public void destroy() {
207        }
208    
209        /**
210         * Return the public interface for the purge jobs service.
211         *
212         * @return {@link PauseTransitService}.
213         */
214        @Override
215        public Class<? extends Service> getInterface() {
216            return PauseTransitService.class;
217        }
218    }