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 org.apache.oozie.client.OozieClient.SYSTEM_MODE;
018    import org.apache.oozie.util.XLog;
019    
020    import java.util.concurrent.Callable;
021    import java.util.concurrent.ScheduledExecutorService;
022    import java.util.concurrent.ScheduledThreadPoolExecutor;
023    import java.util.concurrent.TimeUnit;
024    
025    /**
026     * This service executes scheduled Runnables and Callables at regular intervals. <p/> It uses a
027     * java.util.concurrent.ScheduledExecutorService. <p/> The {@link #SCHEDULER_THREADS} configuration property indicates
028     * how many threads the scheduler will use to run scheduled commands.
029     */
030    public class SchedulerService implements Service {
031    
032        public static final String CONF_PREFIX = Service.CONF_PREFIX + "SchedulerService.";
033    
034        public static final String SCHEDULER_THREADS = CONF_PREFIX + "threads";
035    
036        private final XLog log = XLog.getLog(getClass());
037    
038        private ScheduledExecutorService scheduler;
039    
040        /**
041         * Initialize the scheduler service.
042         *
043         * @param services services instance.
044         */
045        @Override
046        public void init(Services services) {
047            scheduler = new ScheduledThreadPoolExecutor(services.getConf().getInt(SCHEDULER_THREADS, 5));
048        }
049    
050        /**
051         * Destroy the scheduler service.
052         */
053        @Override
054        public void destroy() {
055            try {
056                long limit = System.currentTimeMillis() + 30 * 1000;// 30 seconds
057                scheduler.shutdownNow();
058                while (!scheduler.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
059                    log.info("Waiting for scheduler to shutdown");
060                    if (System.currentTimeMillis() > limit) {
061                        log.warn("Gave up, continuing without waiting for scheduler to shutdown");
062                        break;
063                    }
064                }
065            }
066            catch (InterruptedException ex) {
067                log.warn(ex);
068            }
069        }
070    
071        /**
072         * Return the public interface for scheduler service.
073         *
074         * @return {@link SchedulerService}.
075         */
076        @Override
077        public Class<? extends Service> getInterface() {
078            return SchedulerService.class;
079        }
080    
081        /**
082         * Return the java.util.concurrent.ScheduledExecutorService instance used by the SchedulerService. <p/>
083         *
084         * @return the scheduled executor service instance.
085         */
086        public ScheduledExecutorService getScheduler() {
087            return scheduler;
088        }
089    
090        public enum Unit {
091            MILLISEC(1),
092            SEC(1000),
093            MIN(1000 * 60),
094            HOUR(1000 * 60 * 60);
095    
096            private long millis;
097    
098            private Unit(long millis) {
099                this.millis = millis;
100            }
101    
102            private long getMillis() {
103                return millis;
104            }
105    
106        }
107    
108        /**
109         * Schedule a Callable for execution.
110         *
111         * @param callable callable to schedule for execution.
112         * @param delay delay for first execution since scheduling.
113         * @param interval interval between executions.
114         * @param unit scheduling unit.
115         */
116        public void schedule(final Callable<Void> callable, long delay, long interval, Unit unit) {
117            log.trace("Scheduling callable [{0}], interval [{1}] seconds, delay [{2}] in [{3}]",
118                      callable.getClass(), delay, interval, unit);
119            Runnable r = new Runnable() {
120                public void run() {
121                    if (Services.get().getSystemMode() == SYSTEM_MODE.SAFEMODE) {
122                        log.trace("schedule[run/callable] System is in SAFEMODE. Therefore nothing will run");
123                        return;
124                    }
125                    try {
126                        callable.call();
127                    }
128                    catch (Exception ex) {
129                        log.warn("Error executing callable [{0}], {1}", callable.getClass().getSimpleName(),
130                                 ex.getMessage(), ex);
131                    }
132                }
133            };
134            if (!scheduler.isShutdown()) {
135                scheduler.scheduleWithFixedDelay(r, delay * unit.getMillis(), interval * unit.getMillis(),
136                                                 TimeUnit.MILLISECONDS);
137            }
138            else {
139                log.warn("Scheduler shutting down, ignoring scheduling of [{0}]", callable.getClass());
140            }
141        }
142    
143        /**
144         * Schedule a Runnable for execution.
145         *
146         * @param runnable Runnable to schedule for execution.
147         * @param delay delay for first execution since scheduling.
148         * @param interval interval between executions.
149         * @param unit scheduling unit.
150         */
151        public void schedule(final Runnable runnable, long delay, long interval, Unit unit) {
152            log.trace("Scheduling runnable [{0}], interval [{1}], delay [{2}] in [{3}]",
153                      runnable.getClass(), delay, interval, unit);
154            Runnable r = new Runnable() {
155                public void run() {
156                    if (Services.get().getSystemMode() == SYSTEM_MODE.SAFEMODE) {
157                        log.trace("schedule[run/Runnable] System is in SAFEMODE. Therefore nothing will run");
158                        return;
159                    }
160                    try {
161                        runnable.run();
162                    }
163                    catch (Exception ex) {
164                        log.warn("Error executing runnable [{0}], {1}", runnable.getClass().getSimpleName(),
165                                 ex.getMessage(), ex);
166                    }
167                }
168            };
169            if (!scheduler.isShutdown()) {
170                scheduler.scheduleWithFixedDelay(r, delay * unit.getMillis(), interval * unit.getMillis(),
171                                                     TimeUnit.MILLISECONDS);
172            }
173            else {
174                log.warn("Scheduler shutting down, ignoring scheduling of [{0}]", runnable.getClass());
175            }
176        }
177    
178    }