package org.smartly.commons.async.future; import org.smartly.commons.Delegates; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.*; /** * Act as a timer */ public class Timed { // -------------------------------------------------------------------- // c o n s t a n t s // -------------------------------------------------------------------- /** * If invocations might overlap, you can specify more than a single thread. */ private static final int MAX_THREADS = 1; // -------------------------------------------------------------------- // f i e l d s // -------------------------------------------------------------------- private final List<ScheduledFuture<?>> _tasks; private ScheduledFuture<?> _stop_task; private ScheduledExecutorService __scheduler; private int _count; //-- properties --// private int _max_threads; private TimeUnit _time_unit; private long _initial_delay; private long _interval; private long _stop_after_time; private int _stop_after_count; private boolean _daemon; // -------------------------------------------------------------------- // c o n s t r u c t o r // -------------------------------------------------------------------- public Timed() { this(TimeUnit.MILLISECONDS, 0, 0, 0, 0); // one shot task } public Timed(final long delayBetweenRuns) { this(TimeUnit.SECONDS, 0, delayBetweenRuns, 0, 0); } public Timed(final TimeUnit timeUnit, final long initialDelay, final long delayBetweenRuns, final long stopAfterTime, final int stopAfterCount) { _time_unit = timeUnit; _initial_delay = initialDelay; _interval = delayBetweenRuns; _stop_after_time = stopAfterTime; _stop_after_count = stopAfterCount; _tasks = new LinkedList<ScheduledFuture<?>>(); } // -------------------------------------------------------------------- // p r o p e r t i e s // -------------------------------------------------------------------- public int getMaxThreads() { return _max_threads > 0 ? _max_threads : MAX_THREADS; } /** * Set max number of concurrent threads in pool * @param value int */ public Timed setMaxThreads(final int value) { _max_threads = value > 0 ? value : MAX_THREADS; return this; } public boolean isDaemon() { return _daemon; } /** * Set to true if you want that tasks works as Daemon * @param value boolean */ public Timed setDaemon(final boolean value) { _daemon = value; return this; } public TimeUnit getTimeUnit() { return _time_unit; } public Timed setTimeUnit(final TimeUnit value) { _time_unit = value; return this; } public long getInitialDelay() { return _initial_delay; } public Timed setInitialDelay(final long value) { _initial_delay = value; return this; } public long getInterval() { return _interval; } public Timed setInterval(final long value) { _interval = value; return this; } public long getShutdownAfterTime() { return _stop_after_time; } public Timed setShutdownAfterTime(final long value) { _stop_after_time = value; return this; } public int getShutdownAfterCount() { return _stop_after_count; } public Timed setShutdownAfterCount(final int value) { _stop_after_count = value; return this; } // -------------------------------------------------------------------- // p u b l i c // -------------------------------------------------------------------- /** * Sound the alarm for a few seconds, then stop. */ public ScheduledFuture<?> start(final Delegates.Function<?> callback) { return startTask(callback); } public void stop(final boolean interrupt_if_running) { getScheduler().schedule(new StopTask(this, interrupt_if_running), 0, TimeUnit.MILLISECONDS); } public void join() { synchronized (this) { for (final ScheduledFuture<?> task : _tasks) { try { if (!task.isDone() && !task.isCancelled()) { //task.get(); } } catch (Exception ignored) { } } } } // -------------------------------------------------------------------- // p r i v a t e // -------------------------------------------------------------------- private ScheduledExecutorService getScheduler() { this.init(); return __scheduler; } private void init() { if (null == __scheduler) { __scheduler = Executors.newScheduledThreadPool(this.getMaxThreads(), new Factory(this)); } } private void finish(final boolean interrupt_if_running) { try { for (final ScheduledFuture<?> task : _tasks) { if (!task.isCancelled() || !task.isDone()) { task.cancel(interrupt_if_running); } } } catch (Throwable ignored) { } finally { _tasks.clear(); } //-- stop all tasks--// try { if (null != __scheduler) { if (interrupt_if_running) { __scheduler.shutdownNow(); } else { __scheduler.shutdown(); } } } catch (Throwable ignored) { } finally { __scheduler = null; _stop_task = null; } } private ScheduledFuture<?> startTask(final Delegates.Function<?> callback) { if(_initial_delay==0 && _interval==0){ _stop_after_count = 1; // avoid infinite loop at no interval } final ScheduledFuture<?> future = getScheduler().scheduleWithFixedDelay( new RunTask(this, callback), _initial_delay, _interval, _time_unit ); // add to internal list _tasks.add(future); //-- eval if run count-down--// if (_stop_after_time > 0 && null == _stop_task) { _stop_task = getScheduler().schedule(new StopTask(this, false), _stop_after_time, _time_unit); } return future; } private int incCount() { if (_count == Integer.MAX_VALUE) { _count = 0; // reset counter to avoid errors } _count++; return _count; } private void onTaskFinish() { } // -------------------------------------------------------------------- // S T A T I C // -------------------------------------------------------------------- /** * To start at a specific date in the future, the initial delay * needs to be calculated relative to the current time, as in : * Date futureDate = ... * long startTime = Timed.getRelativeToCurrentTime(futureDate); * Timed alarm = new Timed(startTime, ...); * This works only if the system clock isn't reset. * * @param date Specific Date in the future * @return Millisecond from now to specific Date. */ public static long getRelativeToCurrentTime(final Date date) { return date.getTime() - System.currentTimeMillis(); } // -------------------------------------------------------------------- // M A I N (sample usage) // -------------------------------------------------------------------- /** * Run the example. */ public static void main(String... aArgs) throws InterruptedException { Timed alarmClock = new Timed(TimeUnit.SECONDS, 3, // start after 3 seconds 1, // run each 1 second 0, 0); alarmClock.setMaxThreads(2); // start first thread alarmClock.start(new Delegates.Function<Object>() { @Override public Object handle(Object... args) { final int count = (Integer) args[0]; try { System.out.println("STOPPED BY CODE (sleeping): " + count); Thread.sleep((long) (Math.random() * 1000 * 3)); } catch (Throwable ignored) { } return count < 10; // stop after 10 loop } }); // start second thread alarmClock.start(new Delegates.Function<Object>() { @Override public Object handle(Object... args) { final int count = (Integer) args[0]; System.out.println("STOPPED BY CODE: " + count); return count < 10; // stop after 10 loop } }); Timed alarmClock2 = new Timed(TimeUnit.SECONDS, 0, // start after 3 seconds 1, // run each 1 second 0, 3); alarmClock2.start(new Delegates.Function<Object>() { @Override public Object handle(final Object... args) { final int count = (Integer) args[0]; System.out.println("STOPPED AFTER 3 LOOPS: " + count); return null; } }); } // -------------------------------------------------------------------- // E M B E D D E D // -------------------------------------------------------------------- /** * Thread Factory */ private static final class Factory implements ThreadFactory { private final Timed _sender; public Factory(final Timed sender) { _sender = sender; } @Override public Thread newThread(final Runnable r) { final Thread t = new Thread(r); t.setPriority(Thread.NORM_PRIORITY); t.setDaemon(_sender.isDaemon()); return t; } } /** * Task Runner */ private static final class RunTask implements Runnable { private final Delegates.Function<?> _callback; private final Timed _sender; public RunTask(final Timed sender, final Delegates.Function<?> callback) { _sender = sender; _callback = callback; } @Override public void run() { this.invoke(); } private void invoke() { try { if (null != _sender && null != _callback) { final int count = _sender.incCount(); final Object response = _callback.handle(count, _sender); boolean stop = false; // if false, stop execution of all tasks if (response instanceof Boolean) { if (!((Boolean) response)) { stop = true; } } // if reached max number of iterations, stop final int max_iterations = _sender.getShutdownAfterCount(); if (max_iterations > 0 && count >= max_iterations) { stop = true; } if (stop) { _sender.stop(true); } } } catch (Throwable ignored) { } } } //-- start task /** * Tasks Stopper */ private static final class StopTask implements Runnable { private final Timed _sender; private final boolean _interrupt_if_running; public StopTask(final Timed sender, final boolean interrupt_if_running) { _sender = sender; _interrupt_if_running = interrupt_if_running; } @Override public void run() { if (null != _sender) { _sender.finish(_interrupt_if_running); } } } // stop-task }