package org.infosec.ismp.poller.pollable;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import org.infosec.ismp.model.event.Event;
import org.infosec.ismp.model.poller.PollStatus;
import org.infosec.ismp.util.ThreadCategory;
/**
* Represents a PollableElement
*
* @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a>
*/
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;
protected PollableElement(PollableContainer parent, Scope scope) {
m_parent = parent;
if (parent != null) {
m_cause = parent.getCause();
}
m_scope = scope;
}
public PollableContainer getParent() {
return m_parent;
}
protected void setParent(PollableContainer newParent) {
m_parent = newParent;
}
public Scope getScope() {
return m_scope;
}
public void visit(PollableVisitor v) {
visitThis(v);
}
protected void visitThis(PollableVisitor v) {
v.visitElement(this);
}
public PollStatus getStatus() {
return m_status;
}
private void setStatus(PollStatus status) {
m_status = status;
}
public boolean isStatusChanged() {
return m_statusChanged;
}
private void setStatusChanged(boolean statusChanged) {
m_statusChanged = statusChanged;
}
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);
}
}
public void resetStatusChanged() {
setStatusChanged(false);
}
public void recalculateStatus() {
// do nothing for just an element
}
public abstract PollContext getContext();
/**
* @param service
* @return
*/
public PollStatus doPoll(PollableElement elem) {
if (getParent() == null) {
resetStatusChanged();
return poll(elem);
} else
return getParent().doPoll(elem);
}
public PollableElement getLockRoot() {
PollableContainer parent = getParent();
return (parent == null ? this : parent.getLockRoot());
}
public boolean isTreeLockAvailable() {
return getLockRoot().isTreeLockAvailable();
}
public void obtainTreeLock(long timeout) {
getLockRoot().obtainTreeLock(timeout);
}
public void releaseTreeLock() {
getLockRoot().releaseTreeLock();
}
public void withTreeLock(Runnable r) {
withTreeLock(r, 0);
}
public <T> T withTreeLock(Callable<T> c) {
return withTreeLock(c, 0);
}
public void withTreeLock(Runnable r, long timeout) {
withTreeLock(Executors.callable(r), timeout);
}
public <T> T withTreeLock(Callable<T> c, long timeout) {
try {
obtainTreeLock(timeout);
return c.call();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
releaseTreeLock();
}
}
/**
*
*/
abstract public PollStatus poll();
protected PollStatus poll(PollableElement elem) {
if (elem != this)
throw new IllegalArgumentException("Invalid parameter to poll on "
+ this + ": " + elem);
return poll();
}
/**
* @return
*/
public PollableElement selectPollElement() {
return this;
}
/**
* @param date
* @return
*/
public abstract Event createDownEvent(Date date);
/**
* @param date
* @return
*/
public abstract Event createUpEvent(Date date);
/**
* @param cause TODO
*/
protected void createOutage(PollEvent cause) {
setCause(cause);
}
public boolean hasOpenOutage() {
return m_cause != null;
}
public void setCause(PollEvent cause) {
m_cause = cause;
}
public PollEvent getCause() {
return m_cause;
}
/**
* @param date
*
*/
public void processStatusChange(Date date) {
if (getStatus().isDown() && isStatusChanged()) {
processGoingDown(date);
} else if (getStatus().isUp() && isStatusChanged()) {
processComingUp(date);
}
}
protected void processComingUp(Date date) {
if (getCause() != null) {
PollEvent resolution = getContext().sendEvent(createUpEvent(date));
processResolution(getCause(), resolution);
}
}
protected void processResolution(PollEvent cause, PollEvent resolution) {
// resolveOutage(resolution);
}
protected void processGoingDown(Date date) {
PollEvent cause = getContext().sendEvent(createDownEvent(date));
processCause(cause);
}
protected void processCause(PollEvent cause) {
// if (!hasOpenOutage())
createOutage(cause);
}
protected void resolveAllOutages(PollEvent resolvedCause,
PollEvent resolution) {
// if (resolvedCause.equals(getCause()))
// resolveOutage(resolution);
}
public boolean isDeleted() {
return m_deleted;
}
public void delete() {
Runnable r = new Runnable() {
@Override
public void run() {
m_deleted = true;
if (m_parent != null) {
getParent().deleteMember(PollableElement.this);
getParent().recalculateStatus();
}
}
};
withTreeLock(r);
}
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());
}
}
public PollEvent extrapolateCause() {
return withTreeLock(new Callable<PollEvent>() {
@Override
public PollEvent call() throws Exception {
return doExtrapolateCause();
}
});
}
protected PollEvent doExtrapolateCause() {
return getCause();
}
public void inheritParentalCause() {
withTreeLock(new Runnable() {
@Override
public void run() {
doInheritParentalCause();
}
});
}
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);
}
}
}