gt:                 Double delta = current.doubleValue() - last.value.doubleValue();
context: /*******************************************************************************
   * 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.netmgt.threshd;
  
  import java.io.File;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  import org.opennms.core.utils.ThreadCategory;
  import org.opennms.netmgt.collectd.AliasedResource;
  import org.opennms.netmgt.collectd.IfInfo;
  import org.opennms.netmgt.config.collector.CollectionAttribute;
  import org.opennms.netmgt.config.collector.CollectionResource;
  import org.opennms.netmgt.dao.support.ResourceTypeUtils;
  import org.opennms.netmgt.model.RrdRepository;
  import org.opennms.netmgt.poller.LatencyCollectionResource;
  
  /**
   * <p>CollectionResourceWrapper class.</p>
   * 
   * Wraps a CollectionResource with some methods and caching for the efficient application of thresholds (without
   * pulling thresholding code into CollectionResource itself)
   * 
   * A fresh instance should be created for each collection cycle (assumptions are made based on that premise)
   *
   * @author ranger
   * @version $Id: $
   */
  public class CollectionResourceWrapper {
      
      private int m_nodeId;
      private String m_hostAddress;
      private String m_serviceName;
      private String m_label;
      private String m_iflabel;
      private String m_ifindex;
      private RrdRepository m_repository;
      private CollectionResource m_resource;
      private Map<String, CollectionAttribute> m_attributes;
      
      /**
       * Keeps track of both the Double value, and when it was collected, for the static cache of attributes
       * 
       * This is necessary for the *correct* calculation of Counter rates, across variable collection times and possible
       * collection failures (see NMS-4244)
       * 
       * Just a holder class for two associated values; no need for the formality of accessors
       */
      class CacheEntry {
      	Date timestamp;
      	Double value;
      	public CacheEntry(Date timestamp, Double value) {
      		this.timestamp = timestamp;
      		this.value = value;
      	}
      }
      /*
       * Holds last values for counter attributes (in order to calculate delta)
       */
      static Map<String, CacheEntry> s_cache = new ConcurrentHashMap<String,CacheEntry>();
      
      /*
       * To avoid update static cache on every call of getAttributeValue.
       * In some cases, the same DS could be needed in many thresholds definitions for same resource.
       * See Bug 3193
       */
      private Map<String, Double> m_localCache = new HashMap<String,Double>();
      
      /*
       * Holds interface ifInfo data for interface resource only. This avoid multiple calls to database for same resource.
       */
      private Map<String, String> m_ifInfo;
      
      /*
  	 * Holds the timestamp of the collection being thresholded, for the calculation of counter rates
       */
      private Date m_collectionTimestamp;
          
      /**
       * <p>Constructor for CollectionResourceWrapper.</p>
       *
       * @param interval a long.
       * @param nodeId a int.
       * @param hostAddress a {@link java.lang.String} object.
       * @param serviceName a {@link java.lang.String} object.
       * @param repository a {@link org.opennms.netmgt.model.RrdRepository} object.
       * @param resource a {@link org.opennms.netmgt.config.collector.CollectionResource} object.
       * @param attributes a {@link java.util.Map} object.
       */
      public CollectionResourceWrapper(Date collectionTimestamp, int nodeId, String hostAddress, String serviceName, RrdRepository repository, CollectionResource resource, Map<String, CollectionAttribute> attributes) {
          m_collectionTimestamp = collectionTimestamp;
          m_nodeId = nodeId;
          m_hostAddress = hostAddress;
          m_serviceName = serviceName;
          m_repository = repository;
          m_resource = resource;
          m_attributes = attributes;
          if (isAnInterfaceResource()) {
              if (resource instanceof AliasedResource) { // TODO What about AliasedResource's custom attributes?
                  m_iflabel = ((AliasedResource) resource).getLabel();
                  m_ifInfo = ((AliasedResource) resource).getIfInfo().getAttributesMap();
                  m_ifInfo.put("domain", ((AliasedResource) resource).getDomain());
              }
              if (resource instanceof IfInfo) {
                  m_iflabel = ((IfInfo) resource).getLabel();
                  m_ifInfo = ((IfInfo) resource).getAttributesMap();
              }
              if (resource instanceof LatencyCollectionResource) {
                  JdbcIfInfoGetter ifInfoGetter = new JdbcIfInfoGetter();
                  String ipAddress = ((LatencyCollectionResource) resource).getIpAddress();
                  m_iflabel = ifInfoGetter.getIfLabel(getNodeId(), ipAddress);
                  if (m_iflabel != null) { // See Bug 3488
                      m_ifInfo = ifInfoGetter.getIfInfoForNodeAndLabel(getNodeId(), m_iflabel);
                  } else {
                      log().info("Can't find ifLabel for latency resource " + resource.getInstance() + " on node " + getNodeId());                    
                  }
              }
              if (m_ifInfo != null) {
                  m_ifindex = m_ifInfo.get("snmpifindex");
              } else {
                  log().info("Can't find ifInfo for " + resource);
              }
          }
      }    
      
      /**
       * <p>getNodeId</p>
       *
       * @return a int.
       */
      public int getNodeId() {
          return m_nodeId;
      }
  
      /**
       * <p>getHostAddress</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getHostAddress() {
          return m_hostAddress;
      }
  
      /**
       * <p>getServiceName</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getServiceName() {
          return m_serviceName;
      }
  
      /**
       * <p>getRepository</p>
       *
       * @return a {@link org.opennms.netmgt.model.RrdRepository} object.
       */
      public RrdRepository getRepository() {
          return m_repository;
      }
  
      /**
       * <p>getLabel</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getLabel() {
          return m_label;
      }
  
      /**
       * <p>setLabel</p>
       *
       * @param label a {@link java.lang.String} object.
       */
      public void setLabel(String label) {
          m_label = label;
      }
  
      /**
       * <p>getInstance</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getInstance() {
          return m_resource != null ? m_resource.getInstance() : null;
      }
      
      /**
       * <p>getResourceTypeName</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getResourceTypeName() {
          return m_resource != null ? m_resource.getResourceTypeName() : null;
      }
      
      /**
       * <p>getIfLabel</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getIfLabel() {
          return m_iflabel;
      }
      
      /**
       * <p>getIfIndex</p>
       *
       * @return a {@link java.lang.String} object.
       */
      public String getIfIndex() {
          return m_ifindex;
      }
      
      /**
       * <p>getIfInfoValue</p>
       *
       * @param attribute a {@link java.lang.String} object.
       * @return a {@link java.lang.String} object.
       */
      protected String getIfInfoValue(String attribute) {
          if (m_ifInfo != null)
              return m_ifInfo.get(attribute);
          return null;
      }
      
      /**
       * <p>isAnInterfaceResource</p>
       *
       * @return a boolean.
       */
      public boolean isAnInterfaceResource() {
          return getResourceTypeName() != null && getResourceTypeName().equals("if");
      }
  
      /**
       * <p>isValidInterfaceResource</p>
       *
       * @return a boolean.
       */
      public boolean isValidInterfaceResource() {
          if (m_ifInfo == null) {
              return false;
          }
          try {
              if(null == m_ifindex)
                  return false;
              if(Integer.parseInt(m_ifindex) < 0)
                  return false;
          } catch(Throwable e) {
              return false;
          }
          return true;
      }
  
      /*
       * FIXME What happen with numeric fields from strings.properties ?
       */ 
      /**
       * <p>getAttributeValue</p>
       *
       * @param ds a {@link java.lang.String} object.
       * @return a {@link java.lang.Double} object.
       */
      public Double getAttributeValue(String ds) {
          if (m_attributes == null || m_attributes.get(ds) == null) {
              log().warn("getAttributeValue: can't find attribute called " + ds + " on " + m_resource);
              return null;
          }
          String numValue = m_attributes.get(ds).getNumericValue();
          if (numValue == null) {
              log().warn("getAttributeValue: can't find numeric value for " + ds + " on " + m_resource);
              return null;
          }
          // TODO Is this ID unique ? Here is a suggestion:
          // String id = "node[" + m_nodeId + '].resourceType[' + m_resource.getResourceTypeName()
          //   + '].instance[' + m_resource.getInstance() + "].label[" + m_resource.getLabel()
          //   + "].metric[" + ds + "]"
          String id = m_resource.toString() + "." + ds;
          Double current = Double.parseDouble(numValue);
          if (m_attributes.get(ds).getType().toLowerCase().startsWith("counter") == false) {
              if (log().isDebugEnabled()) {
                  log().debug("getAttributeValue: id=" + id + ", value= " + current);
              }
              return current;
          }
          return getCounterValue(id, current);
      }
  
      /*
       * This will return the rate based on configured collection step
       */
      private Double getCounterValue(String id, Double current) {
          if (m_localCache.containsKey(id) == false) {
              CacheEntry last = s_cache.get(id);
              if (log().isDebugEnabled()) {
                  log().debug("getCounterValue: id=" + id + ", last=" + 
                  		(last==null ? last : last.value +"@"+last.timestamp) + 
                  		", current=" + current);
              }
              s_cache.put(id, new CacheEntry(m_collectionTimestamp, current));
              if (last == null) {
                  m_localCache.put(id, Double.NaN);
                  log().info("getCounterValue: unknown last value, ignoring current");
              } else {                
              	if ( m_collectionTimestamp == null ) {
              		//If you get this, you need to ensure you passed a non-null timestamp to the constructor.  
              		// This usually comes from the CollectionSet that is being visited.
                      log().error("getCounterValue: Haven't got a collection timestamp while calculating a counter for key "+ id + " on " + m_resource
                      		+".  This is a programmer error and should be reported");
              		return null;
              	} 
              	
  