/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2009-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.core.tasks; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.opennms.core.utils.LogUtils; /** * BaseTask * * @author brozow * @version $Id: $ */ public abstract class Task { private static enum State { NEW, SCHEDULED, SUBMITTED, COMPLETED } private final DefaultTaskCoordinator m_coordinator; private final AtomicReference<State> m_state = new AtomicReference<State>(State.NEW); private final AtomicBoolean m_scheduleCalled = new AtomicBoolean(false); private final CountDownLatch m_latch = new CountDownLatch(1); private final AtomicInteger m_pendingPrereqs = new AtomicInteger(0); private final Set<Task> m_dependents = new HashSet<Task>(); private final Set<Task> m_prerequisites = new HashSet<Task>(); private final TaskMonitor m_monitor; /** * <p>Constructor for Task.</p> * * @param coordinator a {@link org.opennms.core.tasks.DefaultTaskCoordinator} object. * @param parent a {@link org.opennms.core.tasks.ContainerTask} object. */ public Task(DefaultTaskCoordinator coordinator, ContainerTask<?> parent) { m_coordinator = coordinator; m_monitor = parent != null ? parent.getMonitor().getChildTaskMonitor(parent, this) : new DefaultTaskMonitor(this); } /** * <p>getCoordinator</p> * * @return a {@link org.opennms.core.tasks.DefaultTaskCoordinator} object. */ public DefaultTaskCoordinator getCoordinator() { return m_coordinator; } /** * <p>getMonitor</p> * * @return a {@link org.opennms.core.tasks.TaskMonitor} object. */ public TaskMonitor getMonitor() { return m_monitor; } /** * These are final and package protected because they should ONLY be accessed by the TaskCoordinator * This is for thread safety and efficiency. use 'addDependency' to update these. */ final Set<Task> getDependents() { return m_dependents; } final void doAddDependent(final Task dependent) { if (!isFinished()) { m_dependents.add(dependent); } } /** * These are final and package protected because they should ONLY be accessed by the TAskCoordinator * This is for thread safety and efficiency. use 'addDependency' to update these */ final Set<Task> getPrerequisites() { return m_prerequisites; } final void doAddPrerequisite(final Task prereq) { if (!prereq.isFinished()) { m_prerequisites.add(prereq); notifyPrerequisteAdded(prereq); } } private void notifyPrerequisteAdded(final Task prereq) { try { m_monitor.prerequisiteAdded(this, prereq); } catch (final Throwable t) { m_monitor.monitorException(t); } } private void notifyPrerequisteCompleted(final Task prereq) { try { m_monitor.prerequisiteCompleted(this, prereq); } catch (final Throwable t) { m_monitor.monitorException(t); } } private void notifyScheduled() { try { m_monitor.scheduled(this); } catch (final Throwable t) { m_monitor.monitorException(t); } } private void notifySubmitted() { try { m_monitor.submitted(this); } catch (final Throwable t) { m_monitor.monitorException(t); } } private void notifyCompleted() { try { m_monitor.completed(this); } catch (final Throwable t) { m_monitor.monitorException(t); } } final void doCompletePrerequisite(final Task prereq) { m_prerequisites.remove(prereq); notifyPrerequisteCompleted(prereq); } final void clearDependents() { m_dependents.clear(); } final void scheduled() { setState(State.NEW, State.SCHEDULED); notifyScheduled(); } private final void setState(final State oldState, final State newState) { if (!m_state.compareAndSet(oldState, newState)) { LogUtils.debugf(this, "Attempted to move to state %s with state not %s (actual value %s)", newState, oldState, m_state.get()); } else { if (LogUtils.isTraceEnabled(this)) { LogUtils.tracef(this, "Set state to %s\n", newState); } } } void submitIfReady() { if (isReady()) { doSubmit(); submitted(); completeSubmit(); } } /** * This method submits a task to be executed and is called when all dependencies are completed for that task * This method should place a runnable on an executor or submit the task in some other way so that it will * run as soon as possible. Tasks that have no processing to be done may override completeSubmit to notify * the Task coordinator that the task is done. */ protected void doSubmit() { } final void submitted() { setState(State.SCHEDULED, State.SUBMITTED); notifySubmitted(); } /** * This method exists to allow a task to have no processing */ protected void completeSubmit() { } final void completed() { m_state.compareAndSet(State.SUBMITTED, State.COMPLETED); notifyCompleted(); } /** * These are final and package protected because they should ONLY be accessed by the TaskCoordinator * This is for thread safety and efficiency. use 'addDependency' to update these */ final boolean isReady() { return isInReadyState() && m_prerequisites.isEmpty() && getPendingPrereqCount() == 0; } private int getPendingPrereqCount() { return m_pendingPrereqs.get(); } private boolean isInReadyState() { return m_state.get() == State.SCHEDULED; } final void incrPendingPrereqCount() { m_pendingPrereqs.incrementAndGet(); } final void decrPendingPrereqCount() { m_pendingPrereqs.decrementAndGet(); } /** * Called from execute after the 'body' of the task has completed */ void onComplete() { completed(); m_latch.countDown(); } /** * This is called to add the task to the queue of tasks that can be considered to be runnable */ public void schedule() { m_scheduleCalled.set(true); preSchedule(); getCoordinator().schedule(this); postSchedule(); } /** * <p>preSchedule</p> */ protected void preSchedule() { } /** * <p>postSchedule</p> */ protected void postSchedule() { } /** * This task's run method has completed * * @return a boolean. */ public boolean isFinished() { return m_state.get() == State.COMPLETED; } /** * This task has be sent to the TaskCoordinator to be run * * @return a boolean. */ public boolean isScheduled() { return m_state.get() != State.NEW || m_scheduleCalled.get(); } /** * Add's prereq as a Prerequisite of this task. In other words... this taks cannot run * until prereq was been complted. * * @param prereq a {@link org.opennms.core.tasks.Task} object. */ public void addPrerequisite(final Task prereq) { getCoordinator().addDependency(prereq, this); } /** * Adds dependent as a dependent of this task. So dependent will not be able to run * until this task has been completed. * * @param dependent a {@link org.opennms.core.tasks.Task} object. */ public void addDependent(final Task dependent) { getCoordinator().addDependency(this, dependent); } /** * Wait for this task to complete. The current thread will block until this task has been completed. * * @throws java.lang.InterruptedException if any. * @throws java.util.concurrent.ExecutionException if any. */ public void waitFor() throws InterruptedException, ExecutionException { m_latch.await(); } /** * Wait for this task to complete or until a timeout occurs * * @param timeout a long. * @param unit a {@link java.util.concurrent.TimeUnit} object. * @throws java.lang.InterruptedException if any. */ public void waitFor(final long timeout, final TimeUnit unit) throws InterruptedException { m_latch.await(timeout, unit); } /** * <p>markTaskAsCompleted</p> */ protected void markTaskAsCompleted() { getCoordinator().markTaskAsCompleted(this); } /** * <p>submitRunnable</p> * * @param runnable a {@link java.lang.Runnable} object. * @param preferredExecutor a {@link java.lang.String} object. */ protected void submitRunnable(Runnable runnable, String preferredExecutor) { getCoordinator().submitToExecutor(preferredExecutor, runnable, this); } /** * <p>toString</p> * * @return a {@link java.lang.String} object. */ public String toString() { return String.format("Task[%s]", super.toString()); } /** * <p>info</p> * * @param format a {@link java.lang.String} object. * @param args a {@link java.lang.Object} object. */ protected void info(final String format, final Object... args) { LogUtils.infof(this, format, args); } /** * <p>debug</p> * * @param format a {@link java.lang.String} object. * @param args a {@link java.lang.Object} object. */ protected void debug(String format, Object... args) { LogUtils.debugf(this, format, args); } }