blob: 512e8a80e838a37b67070fc0d4e44d8a2fbbe84b [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.felix.useradmin.filestore;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Provides a timer that can be reset.
*/
final class ResettableTimer {
private final ScheduledExecutorService m_executor;
private final Runnable m_task;
private final long m_timeout;
private final TimeUnit m_timeUnit;
private final AtomicReference m_futureRef;
/**
* Creates a new {@link ResettableTimer} calling a given task when a given
* timeout exceeds.
*
* @param task the task to execute upon timout, cannot be <code>null</code>;
* @param timeout the timeout value, > 0;
* @param unit the time unit of the timeout value, cannot be <code>null</code>.
*/
public ResettableTimer(Runnable task, long timeout, TimeUnit unit) {
this(new ScheduledThreadPoolExecutor(1), task, timeout, unit);
}
/**
* Creates a new {@link ResettableTimer} calling a given task when a given
* timeout exceeds.
*
* @param executor the executor to use to execute the task, cannot be <code>null</code>;
* @param task the task to execute upon timout, cannot be <code>null</code>;
* @param timeout the timeout value, > 0;
* @param unit the time unit of the timeout value, cannot be <code>null</code>.
*/
public ResettableTimer(ScheduledExecutorService executor, Runnable task, long timeout, TimeUnit unit) {
if (executor == null) {
throw new IllegalArgumentException("Executor cannot be null!");
}
if (task == null) {
throw new IllegalArgumentException("Task cannot be null!");
}
if (timeout <= 0) {
throw new IllegalArgumentException("Timeout cannot be negative!");
}
if (unit == null) {
throw new IllegalArgumentException("TimeUnit cannot be null!");
}
m_executor = executor;
m_task = task;
m_timeout = timeout;
m_timeUnit = unit;
m_futureRef = new AtomicReference();
}
/**
* Returns the state of this timer.
*
* @return <code>true</code> if this timer is shut down, <code>false</code> otherwise.
*/
public boolean isShutDown() {
return m_executor.isShutdown();
}
/**
* Schedules the task for execution with the contained timeout. If a task
* is already pending or running, it will be cancelled (not interrupted).
* The new task will be scheduled to run in now + timeout.
*/
public ScheduledFuture schedule() {
ScheduledFuture currentTask = cancelCurrentTask();
ScheduledFuture newTask = m_executor.schedule(m_task, m_timeout, m_timeUnit);
m_futureRef.compareAndSet(currentTask, newTask);
return newTask;
}
/**
* Shuts down this timer, allowing any pending tasks to execute. After this
* method is called, {@link #schedule()} may no longer be called.
*/
public void shutDown() {
m_executor.shutdown();
try {
m_executor.awaitTermination(2 * m_timeout, m_timeUnit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* @return the current task, or <code>null</code> if no task is available.
*/
private ScheduledFuture cancelCurrentTask() {
ScheduledFuture currentTask = (ScheduledFuture) m_futureRef.get();
if (currentTask != null) {
// Doesn't matter for completed tasks...
currentTask.cancel(false /* mayInterruptIfRunning */);
}
return currentTask;
}
}