/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2010-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.web.category;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.ValidationException;
import org.opennms.core.utils.ThreadCategory;
import org.opennms.netmgt.xml.rtc.Node;
/**
* Adapts the functionality of the category definition and RTC category updates
* into one simple interface. Also adds many convenience methods.
*
* <p>
* The category definition is read from the categories.xml file by the
* {@link org.opennms.netmgt.config.CategoryFactory CategoryFactory}. The RTC
* category updates are periodically sent from the RTC to the WebUI.
* </p>
*
* @author <a href="mailto:larry@opennms.org">Lawrence Karnowski </a>
* @author <a href="http://www.opennms.org/">OpenNMS </a>
*/
public class Category {
/** The category definition (from the categories.xml file). */
protected final org.opennms.netmgt.config.categories.Category m_categoryDef;
/**
* An update from the RTC about the service level availability for this
* category.
*/
protected final org.opennms.netmgt.xml.rtc.Category m_rtcCategory;
/**
* The last time this category was updated. Note that with the current way
* this class and the CategoryModel are implemented, this value does not
* change because a new instance of this class is created for each RTC
* update.
*/
protected final Date m_lastUpdated;
/**
* A cached value of the total number of services on nodes belonging to this
* category.
*/
protected Long m_serviceCount;
/**
* A cached value of the total number of services on nodes belonging to this
* category that are currently down.
*/
protected Long m_serviceDownCount;
/**
* A cached value of the ratio of services that are up on notes belonging to
* this category to all nodes belonging in this category.
*/
protected Double m_servicePercentage;
/**
* Create an empty category with nothing other than a name. This represents
* a category with no RTC data.
*
* @param categoryName a {@link java.lang.String} object.
*/
protected Category(String categoryName) {
m_categoryDef = new org.opennms.netmgt.config.categories.Category();
m_categoryDef.setLabel(categoryName);
m_rtcCategory = null;
m_lastUpdated = null;
}
/**
* Create a new instance to wrapper information from the categories.xml file
* (that defines a category) and information from the RTC (that gives
* current service level availability).
*
* @param categoryDef a {@link org.opennms.netmgt.config.categories.Category} object.
* @param rtcCategory a {@link org.opennms.netmgt.xml.rtc.Category} object.
* @param lastUpdated a {@link java.util.Date} object.
*/
protected Category(final org.opennms.netmgt.config.categories.Category categoryDef, final org.opennms.netmgt.xml.rtc.Category rtcCategory, final Date lastUpdated) {
if (categoryDef == null || rtcCategory == null || lastUpdated == null) {
throw new IllegalArgumentException("Cannot take null parameters.");
}
if (categoryDef.getLabel() == null || !categoryDef.getLabel().equals(rtcCategory.getCatlabel())) {
throw new IllegalArgumentException("Cannot take category " + "definition and rtc category " + "value whose names do not " + "match.");
}
m_categoryDef = categoryDef;
m_rtcCategory = rtcCategory;
m_lastUpdated = lastUpdated;
m_serviceCount = null;
m_serviceDownCount = null;
m_servicePercentage = null;
}
/**
* Return the unique name for this category.
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return m_categoryDef.getLabel();
}
/**
* Return the value considered to be the minimum "normal" value.
*
* @return a double.
*/
public double getNormalThreshold() {
return m_categoryDef.getNormal();
}
/**
* Return the value considered to be the minimum value below the "normal"
* value where only a warning is necessary. Below this value the category's
* value will be considered unacceptable.
*
* @return a double.
*/
public double getWarningThreshold() {
return m_categoryDef.getWarning();
}
/**
* Return a description explaining this category.
*
* @return a {@link java.lang.String} object.
*/
public String getComment() {
return m_categoryDef.getComment();
}
/**
* Return the date and time this category was last updated by the RTC.
*
* @return a {@link java.util.Date} object.
*/
public Date getLastUpdated() {
return m_lastUpdated;
}
/**
* Return the current service level availability for this category.
*
* @return a double.
*/
public double getValue() {
if (m_rtcCategory == null) {
return 0.0;
} else {
return m_rtcCategory.getCatvalue();
}
}
/**
* Package protected implementation method that exposes the internal
* representation (a Castor-generated object) of the data from the RTC,
* strictly for use in marshalling the data back to XML (via Castor). In
* other words, this method is only for debugging purposes, please do not
* use in normal situations. Instead please use the public methods of this
* class.
*/
org.opennms.netmgt.xml.rtc.Category getRtcCategory() {
return m_rtcCategory;
}
/**
* Return the number of services contained within this category. This is
* synchronized because it updates several instance variables as it executes.
*
* @return a long.
*/
private synchronized long getServiceCount() {
if (m_serviceCount == null) {
if (m_rtcCategory == null) {
m_serviceCount = new Long(0);
m_serviceDownCount = new Long(0);
m_servicePercentage = new Double(0);
} else {
long[] counts = getServiceCounts(m_rtcCategory);
m_serviceCount = new Long(counts[0]);
m_serviceDownCount = new Long(counts[1]);
if (m_serviceCount.longValue() == 0) {
m_servicePercentage = new Double(100.0);
} else {
m_servicePercentage = new Double(((double) (m_serviceCount.longValue() - m_serviceDownCount.longValue())) / (double) m_serviceCount.longValue() * 100.0);
}
}
}
return m_serviceCount.longValue();
}
/**
* Return the number of services that are currently down with this category.
*
* @return a long.
*/
public synchronized long getServiceDownCount() {
if (m_serviceDownCount == null) {
// This will initialize m_serviceDownCount
getServiceCount();
}
if (m_serviceDownCount == null) {
ThreadCategory.getInstance(this.getClass()).warn("Could not fetch service down count for category: " + m_rtcCategory.getCatlabel());
return 0;
} else {
return m_serviceDownCount.longValue();
}
}
/**
* Return a percentage of the ratio of services that are up to all services
* in this category.
*
* @return a double.
*/
public synchronized double getServicePercentage() {
if (m_servicePercentage == null) {
// This will initialize m_servicePercentage
getServiceCount();
}
if (m_servicePercentage == null) {
ThreadCategory.getInstance(this.getClass()).warn("Could not fetch service percentage for category: " + m_rtcCategory.getCatlabel());
return 0.0;
} else {
return m_servicePercentage.doubleValue();
}
}
/**
* Returns the outage background color for this category.
*
* @return a {@link java.lang.String} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public String getOutageColor() throws IOException, MarshalException, ValidationException {
if (m_lastUpdated == null) {
return "lightblue";
} else {
return CategoryUtil.getCategoryColor(this, getServicePercentage());
}
}
/**
* Returns the availability background color for this category.
*
* @return a {@link java.lang.String} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public String getAvailColor() throws IOException, MarshalException, ValidationException {
if (m_lastUpdated == null) {
return "lightblue";
} else {
return CategoryUtil.getCategoryColor(this);
}
}
/**
* Returns the outage CSS class for this category.
*
* @return a {@link java.lang.String} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public String getOutageClass() throws IOException, MarshalException, ValidationException {
if (m_lastUpdated == null) {
return "lightblue";
} else {
return CategoryUtil.getCategoryClass(this, getServicePercentage());
}
}
/**
* Returns the availability CSS class for this category.
*
* @return a {@link java.lang.String} object.
* @throws java.io.IOException if any.
* @throws org.exolab.castor.xml.MarshalException if any.
* @throws org.exolab.castor.xml.ValidationException if any.
*/
public String getAvailClass() throws IOException, MarshalException, ValidationException {
if (m_lastUpdated == null) {
return "lightblue";
} else {
return CategoryUtil.getCategoryClass(this);
}
}
/**
* Returns the outage text for this category ("X of Y" nodes down).
*
* @return a {@link java.lang.String} object.
*/
public String getOutageText() {
if (m_lastUpdated == null) {
return "Calculating...";
} else {
return getServiceDownCount() + " of " + getServiceCount();
}
}
/**
* Returns the availability text for this category ("XXX.XX%").
*
* @return a {@link java.lang.String} object.
*/
public String getAvailText() {
if (m_lastUpdated == null) {
return "Calculating...";
} else {
return CategoryUtil.valueFormat.format(getValue()) + "%";
}
}
/**
* Returns the category comment if there is one, otherwise, its name.
*
* @return a {@link java.lang.String} object.
*/
public String getTitle() {
if (getComment() != null) {
return getComment();
} else {
return getName();
}
}
/**
* Returns an enumeration of the Castor-generated Node objects tied to this
* category.
*
* <p>
* Note, LJK Dec 5,2001: I'm not really happy about exposing the Castor
* objects this way. We do it all over the place, but I've already started
* hiding them in this particular case (the rtceui.xsd objects). I'm not
* very pleased with this half approach. I'd rather hide them completely or
* not at all, but I don't want to introduce a new pass-through object.
* </p>
*
* @return a {@link java.util.Enumeration} object.
*/
public Enumeration<Node> enumerateNode() {
return m_rtcCategory.enumerateNode();
}
/**
* Convenience method to count the number of services under a category and
* the number of those services that are currently down.
*
* @param category a {@link org.opennms.netmgt.xml.rtc.Category} object.
* @return an array of long.
*/
protected static long[] getServiceCounts(final org.opennms.netmgt.xml.rtc.Category category) {
if (category == null) {
throw new IllegalArgumentException("Cannot take null parameters.");
}
long count = 0;
long downCount = 0;
Enumeration<Node> nodeEnum = category.enumerateNode();
while (nodeEnum.hasMoreElements()) {
org.opennms.netmgt.xml.rtc.Node node = nodeEnum.nextElement();
count += node.getNodesvccount();
downCount += node.getNodesvcdowncount();
}
return new long[] { count, downCount };
}
}