| /** |
| * 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; |
| } |
| } |