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
019package org.apache.hadoop.lib.service.scheduler;
020
021import org.apache.hadoop.lib.lang.RunnableCallable;
022import org.apache.hadoop.lib.server.BaseService;
023import org.apache.hadoop.lib.server.Server;
024import org.apache.hadoop.lib.server.ServiceException;
025import org.apache.hadoop.lib.service.Instrumentation;
026import org.apache.hadoop.lib.service.Scheduler;
027import org.apache.hadoop.lib.util.Check;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import java.text.MessageFormat;
032import java.util.concurrent.Callable;
033import java.util.concurrent.ScheduledExecutorService;
034import java.util.concurrent.ScheduledThreadPoolExecutor;
035import java.util.concurrent.TimeUnit;
036
037public 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}