/** * Copyright (C) 2012-2017 the original author or authors. * * 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. */ package ninja.scheduler; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import ninja.lifecycle.Dispose; import ninja.lifecycle.Start; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.ConfigurationException; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Singleton; import com.google.inject.name.Names; /** * The actual scheduler */ @Singleton public class Scheduler { private static final Logger log = LoggerFactory.getLogger(Scheduler.class); @Inject private Injector injector; private volatile ScheduledExecutorService executor; private final List<Object> objectsToSchedule = Collections.synchronizedList(new ArrayList<Object>()); @Start(order = 90) public void start() { executor = Executors.newSingleThreadScheduledExecutor(); scheduleCachedObjects(); } @Dispose(order = 90) public void dispose() { executor.shutdown(); executor = null; } public boolean hasScheduledMethod(Class<?> clazz) { for (Method method : clazz.getMethods()) { Schedule schedule = method.getAnnotation(Schedule.class); if (schedule != null) { return true; } } return false; } public void schedule(Object target) { if (executor == null) { objectsToSchedule.add(target); } else { for (final Method method : target.getClass().getMethods()) { Schedule schedule = method.getAnnotation(Schedule.class); if (schedule != null) { schedule(target, method, schedule); } } } } private void scheduleCachedObjects() { List<Object> copy = new ArrayList<Object>(objectsToSchedule); objectsToSchedule.clear(); for (Object object : copy) { schedule(object); } } private void schedule(final Object target, final Method method, Schedule schedule) { long delay = schedule.delay(); if (!schedule.delayProperty().equals(Schedule.NO_PROPERTY)) { String delayString = getProperty(schedule.delayProperty()); if (delayString != null) { delay = Long.parseLong(delayString); } else if (delay < 0) { throw new IllegalArgumentException("No delay property found: " + schedule.delayProperty() + " and no default delay set"); } } if (delay < 0) { throw new IllegalArgumentException("No delay or delay property specified"); } TimeUnit timeUnit = schedule.timeUnit(); if (!schedule.timeUnitProperty().equals(Schedule.NO_PROPERTY)) { String timeUnitString = getProperty(schedule.timeUnitProperty()); if (timeUnitString != null) { timeUnit = TimeUnit.valueOf(timeUnitString); } } long initialDelay = schedule.initialDelay(); if (!schedule.initialDelayProperty().equals(Schedule.NO_PROPERTY)) { String initialDelayString = getProperty(schedule.initialDelayProperty()); if (initialDelayString != null) { initialDelay = Long.parseLong(initialDelayString); } } if (initialDelay < 0) { initialDelay = delay; } log.info("Scheduling method " + method.getName() + " on " + target + " to be run every " + delay + " " + timeUnit + " after " + initialDelay + " " + timeUnit); executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { log.debug("Running scheduled method {} on {}", method.getName(), target); method.invoke(target); } catch (Exception e) { log.error("Error invoking scheduled run of method " + method.getName() + " on " + target, e); } } }, initialDelay, delay, timeUnit); } private String getProperty(String name) { try { return injector.getInstance(Key.get(String.class, Names.named(name))); } catch (ConfigurationException e) { // Ignore return null; } } }