/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-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.netmgt.poller.pollables; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.model.PollStatus; import org.opennms.netmgt.xml.event.Event; /** * Represents a PollableElement * * @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a> * @version $Id: $ */ abstract public class PollableElement { private final Scope m_scope; private volatile PollableContainer m_parent; private volatile PollStatus m_status = PollStatus.unknown(); private volatile boolean m_statusChanged = false; private volatile PollEvent m_cause; private volatile boolean m_deleted; /** * <p>Constructor for PollableElement.</p> * * @param parent a {@link org.opennms.netmgt.poller.pollables.PollableContainer} object. * @param scope a {@link org.opennms.netmgt.poller.pollables.Scope} object. */ protected PollableElement(PollableContainer parent, Scope scope) { m_parent = parent; if (parent != null) { m_cause = parent.getCause(); } m_scope = scope; } /** * <p>getParent</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollableContainer} object. */ public PollableContainer getParent() { return m_parent; } /** * <p>setParent</p> * * @param newParent a {@link org.opennms.netmgt.poller.pollables.PollableContainer} object. */ protected void setParent(PollableContainer newParent) { m_parent = newParent; } /** * <p>getScope</p> * * @return a {@link org.opennms.netmgt.poller.pollables.Scope} object. */ public Scope getScope() { return m_scope; } /** * <p>visit</p> * * @param v a {@link org.opennms.netmgt.poller.pollables.PollableVisitor} object. */ public void visit(PollableVisitor v) { visitThis(v); } /** * <p>visitThis</p> * * @param v a {@link org.opennms.netmgt.poller.pollables.PollableVisitor} object. */ protected void visitThis(PollableVisitor v) { v.visitElement(this); } /** * <p>getStatus</p> * * @return a {@link org.opennms.netmgt.model.PollStatus} object. */ public PollStatus getStatus() { return m_status; } private void setStatus(PollStatus status) { m_status = status; } /** * <p>isStatusChanged</p> * * @return a boolean. */ public boolean isStatusChanged() { return m_statusChanged; } private void setStatusChanged(boolean statusChanged) { m_statusChanged = statusChanged; } /** * <p>updateStatus</p> * * @param newStatus a {@link org.opennms.netmgt.model.PollStatus} object. */ public void updateStatus(PollStatus newStatus) { PollStatus oldStatus = getStatus(); if (!oldStatus.equals(newStatus)) { ThreadCategory.getInstance(getClass()).info("Changing status of PollableElement "+this+" from "+oldStatus+" to "+newStatus); setStatus(newStatus); setStatusChanged(true); } } /** * <p>resetStatusChanged</p> */ public void resetStatusChanged() { setStatusChanged(false); } /** * <p>recalculateStatus</p> */ public void recalculateStatus() { // do nothing for just an element } /** * <p>getContext</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollContext} object. */ public abstract PollContext getContext(); /** * <p>doPoll</p> * * @param elem a {@link org.opennms.netmgt.poller.pollables.PollableElement} object. * @return a {@link org.opennms.netmgt.model.PollStatus} object. */ public PollStatus doPoll(PollableElement elem) { if (getParent() == null) { resetStatusChanged(); return poll(elem); } else return getParent().doPoll(elem); } /** * <p>getLockRoot</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollableElement} object. */ public PollableElement getLockRoot() { PollableContainer parent = getParent(); return (parent == null ? this : parent.getLockRoot()); } /** * <p>isTreeLockAvailable</p> * * @return a boolean. */ public boolean isTreeLockAvailable() { return getLockRoot().isTreeLockAvailable(); } /** * <p>obtainTreeLock</p> * * @param timeout a long. */ public void obtainTreeLock(long timeout) { getLockRoot().obtainTreeLock(timeout); } /** * <p>releaseTreeLock</p> */ public void releaseTreeLock() { getLockRoot().releaseTreeLock(); } /** * <p>withTreeLock</p> * * @param r a {@link java.lang.Runnable} object. */ public void withTreeLock(Runnable r) { withTreeLock(r, 0); } /** * <p>withTreeLock</p> * * @param c a {@link java.util.concurrent.Callable} object. * @param <T> a T object. * @return a T object. */ public <T> T withTreeLock(Callable<T> c) { return withTreeLock(c, 0); } /** * <p>withTreeLock</p> * * @param r a {@link java.lang.Runnable} object. * @param timeout a long. */ public void withTreeLock(Runnable r, long timeout) { withTreeLock(Executors.callable(r), timeout); } /** * <p>withTreeLock</p> * * @param c a {@link java.util.concurrent.Callable} object. * @param timeout a long. * @param <T> a T object. * @return a T object. */ public <T> T withTreeLock(Callable<T> c, long timeout) { try { obtainTreeLock(timeout); return c.call(); } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } finally { releaseTreeLock(); } } /** * <p>poll</p> * * @return a {@link org.opennms.netmgt.model.PollStatus} object. */ abstract public PollStatus poll(); /** * <p>poll</p> * * @param elem a {@link org.opennms.netmgt.poller.pollables.PollableElement} object. * @return a {@link org.opennms.netmgt.model.PollStatus} object. */ protected PollStatus poll(PollableElement elem) { if (elem != this) throw new IllegalArgumentException("Invalid parameter to poll on "+this+": "+elem); return poll(); } /** * <p>selectPollElement</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollableElement} object. */ public PollableElement selectPollElement() { return this; } /** * <p>createDownEvent</p> * * @param date a {@link java.util.Date} object. * @return a {@link org.opennms.netmgt.xml.event.Event} object. */ public abstract Event createDownEvent(Date date); /** * <p>createUpEvent</p> * * @param date a {@link java.util.Date} object. * @return a {@link org.opennms.netmgt.xml.event.Event} object. */ public abstract Event createUpEvent(Date date); /** * <p>createOutage</p> * * @param cause TODO */ protected void createOutage(PollEvent cause) { setCause(cause); } /** * <p>resolveOutage</p> * * @param resolution TODO */ protected void resolveOutage(PollEvent resolution) { setCause(null); } /** * <p>hasOpenOutage</p> * * @return a boolean. */ public boolean hasOpenOutage() { return m_cause != null; } /** * <p>setCause</p> * * @param cause a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ public void setCause(PollEvent cause) { m_cause = cause; } /** * <p>getCause</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ public PollEvent getCause() { return m_cause; } /** * <p>processStatusChange</p> * * @param date a {@link java.util.Date} object. */ public void processStatusChange(Date date) { if (getStatus().isDown() && isStatusChanged()) { processGoingDown(date); } else if (getStatus().isUp() && isStatusChanged()) { processComingUp(date); } } /** * <p>processComingUp</p> * * @param date a {@link java.util.Date} object. */ protected void processComingUp(Date date) { if (getCause() != null) { PollEvent resolution = getContext().sendEvent(createUpEvent(date)); processResolution(getCause(), resolution); } } /** * <p>processResolution</p> * * @param cause a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. * @param resolution a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ protected void processResolution(PollEvent cause, PollEvent resolution) { resolveOutage(resolution); } /** * <p>processGoingDown</p> * * @param date a {@link java.util.Date} object. */ protected void processGoingDown(Date date) { PollEvent cause = getContext().sendEvent(createDownEvent(date)); processCause(cause); } /** * <p>processCause</p> * * @param cause a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ protected void processCause(PollEvent cause) { if (!hasOpenOutage()) createOutage(cause); } /** * <p>resolveAllOutages</p> * * @param resolvedCause a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. * @param resolution a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ protected void resolveAllOutages(PollEvent resolvedCause, PollEvent resolution) { if (resolvedCause.equals(getCause())) resolveOutage(resolution); } /** * <p>isDeleted</p> * * @return a boolean. */ public boolean isDeleted() { return m_deleted; } /** * <p>delete</p> */ public void delete() { Runnable r = new Runnable() { public void run() { m_deleted = true; if (m_parent != null) { getParent().deleteMember(PollableElement.this); getParent().recalculateStatus(); } } }; withTreeLock(r); } /** * <p>processLingeringCauses</p> * * @param resolvedCause a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. * @param resolution a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ protected void processLingeringCauses(PollEvent resolvedCause, PollEvent resolution) { if (getStatus().isDown() && resolvedCause.equals(getCause())) { resolveAllOutages(resolvedCause, resolution); processGoingDown(resolution.getDate()); } else if (getStatus().isUp() && resolvedCause.equals(getCause())) { processResolution(resolvedCause, resolution); } else if (getStatus().isUp() && !resolvedCause.equals(getCause())) { processComingUp(resolution.getDate()); } } /** * <p>extrapolateCause</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ public PollEvent extrapolateCause() { return withTreeLock(new Callable<PollEvent>() { public PollEvent call() throws Exception { return doExtrapolateCause(); } }); } /** * <p>doExtrapolateCause</p> * * @return a {@link org.opennms.netmgt.poller.pollables.PollEvent} object. */ protected PollEvent doExtrapolateCause() { return getCause(); } /** * <p>inheritParentalCause</p> */ public void inheritParentalCause() { withTreeLock(new Runnable() { public void run() { doInheritParentalCause(); } }); } /** * <p>doInheritParentalCause</p> */ protected void doInheritParentalCause() { if (getParent() == null) return; PollEvent parentalCause = getParent().getCause(); PollStatus parentalStatus = getParent().getStatus(); if (parentalCause == null) { // parent has no cause so no need to do anything here return; } if (getCause() == null || getCause().hasScopeLargerThan(getScope())) { // I have no cause but my parent is down.. mark me as down as well // I already have a cause that's larger than myself then inherit as well setCause(parentalCause); updateStatus(parentalStatus); } } }