001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package org.apache.hadoop.lib.service.scheduler;
020    
021    import org.apache.hadoop.lib.lang.RunnableCallable;
022    import org.apache.hadoop.lib.server.BaseService;
023    import org.apache.hadoop.lib.server.Server;
024    import org.apache.hadoop.lib.server.ServiceException;
025    import org.apache.hadoop.lib.service.Instrumentation;
026    import org.apache.hadoop.lib.service.Scheduler;
027    import org.apache.hadoop.lib.util.Check;
028    import org.slf4j.Logger;
029    import org.slf4j.LoggerFactory;
030    
031    import java.text.MessageFormat;
032    import java.util.concurrent.Callable;
033    import java.util.concurrent.ScheduledExecutorService;
034    import java.util.concurrent.ScheduledThreadPoolExecutor;
035    import java.util.concurrent.TimeUnit;
036    
037    public class SchedulerService extends BaseService implements Scheduler {
038      private static final Logger LOG = LoggerFactory.getLogger(SchedulerService.class);
039    
040      private static final String INST_GROUP = "scheduler";
041    
042      public static final String PREFIX = "scheduler";
043    
044      public static final String CONF_THREADS = "threads";
045    
046      private ScheduledExecutorService scheduler;
047    
048      public SchedulerService() {
049        super(PREFIX);
050      }
051    
052      @Override
053      public void init() throws ServiceException {
054        int threads = getServiceConfig().getInt(CONF_THREADS, 5);
055        scheduler = new ScheduledThreadPoolExecutor(threads);
056        LOG.debug("Scheduler started");
057      }
058    
059      @Override
060      public void destroy() {
061        try {
062          long limit = System.currentTimeMillis() + 30 * 1000;
063          scheduler.shutdownNow();
064          while (!scheduler.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
065            LOG.debug("Waiting for scheduler to shutdown");
066            if (System.currentTimeMillis() > limit) {
067              LOG.warn("Gave up waiting for scheduler to shutdown");
068              break;
069            }
070          }
071          if (scheduler.isTerminated()) {
072            LOG.debug("Scheduler shutdown");
073          }
074        } catch (InterruptedException ex) {
075          LOG.warn(ex.getMessage(), ex);
076        }
077      }
078    
079      @Override
080      public Class[] getServiceDependencies() {
081        return new Class[]{Instrumentation.class};
082      }
083    
084      @Override
085      public Class getInterface() {
086        return Scheduler.class;
087      }
088    
089      @Override
090      public void schedule(final Callable<?> callable, long delay, long interval, TimeUnit unit) {
091        Check.notNull(callable, "callable");
092        if (!scheduler.isShutdown()) {
093          LOG.debug("Scheduling callable [{}], interval [{}] seconds, delay [{}] in [{}]",
094                    new Object[]{callable, delay, interval, unit});
095          Runnable r = new Runnable() {
096            public void run() {
097              String instrName = callable.getClass().getSimpleName();
098              Instrumentation instr = getServer().get(Instrumentation.class);
099              if (getServer().getStatus() == Server.Status.HALTED) {
100                LOG.debug("Skipping [{}], server status [{}]", callable, getServer().getStatus());
101                instr.incr(INST_GROUP, instrName + ".skips", 1);
102              } else {
103                LOG.debug("Executing [{}]", callable);
104                instr.incr(INST_GROUP, instrName + ".execs", 1);
105                Instrumentation.Cron cron = instr.createCron().start();
106                try {
107                  callable.call();
108                } catch (Exception ex) {
109                  instr.incr(INST_GROUP, instrName + ".fails", 1);
110                  LOG.error("Error executing [{}], {}", new Object[]{callable, ex.getMessage(), ex});
111                } finally {
112                  instr.addCron(INST_GROUP, instrName, cron.stop());
113                }
114              }
115            }
116          };
117          scheduler.scheduleWithFixedDelay(r, delay, interval, unit);
118        } else {
119          throw new IllegalStateException(
120            MessageFormat.format("Scheduler shutting down, ignoring scheduling of [{}]", callable));
121        }
122      }
123    
124      @Override
125      public void schedule(Runnable runnable, long delay, long interval, TimeUnit unit) {
126        schedule((Callable<?>) new RunnableCallable(runnable), delay, interval, unit);
127      }
128    
129    }