SnmpRequestSensitiveDispatchCollection.java

package eu.linuxengineering.snmp;

import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import eu.javaexperience.collection.map.KeyVal;
import eu.javaexperience.interfaces.simple.getBy.GetBy1;
import eu.javaexperience.log.JavaExperienceLoggingFacility;
import eu.javaexperience.log.LogLevel;
import eu.javaexperience.log.Loggable;
import eu.javaexperience.log.Logger;
import eu.javaexperience.log.LoggingTools;
import net.sf.snmpadaptor4j.api.AttributeAccessor;
import net.sf.snmpadaptor4j.object.SnmpOid;

public class SnmpRequestSensitiveDispatchCollection implements SnmpNode
{
	protected static final Logger LOG = JavaExperienceLoggingFacility.getLogger(new Loggable("SnmpRequestSensitiveDispatchCollection"));
	
	protected ConcurrentMap<SnmpOid, Entry<Integer, SnmpNode>> nodeCache = new ConcurrentHashMap<>();
	
	protected NavigableMap<Integer, GetBy1<SnmpNode, SnmpOid>> nodeFactories = Collections.synchronizedNavigableMap
	(
		new TreeMap<>()
	);
	
	public SnmpRequestSensitiveDispatchCollection(){}
	
	public void addNodeFactory
	(
		Integer index,
		GetBy1<SnmpNode, SnmpOid> factory
	)
	{
		nodeFactories.put(index, factory);
	}
	
	public void addNodeFactory
	(
		Integer index,
		SnmpNode node
	)
	{
		nodeFactories.put(index, (a)->node);
	}
	
	@Override
	public Entry<Integer, SnmpNode> getSubNodeGte(SnmpPathDispatch dispatch)
	{
		SnmpOid oid = SnmpOid.newInstance(dispatch.copyCurrentPath());
		dispatch.jumpNextNode();
		Entry<Integer, SnmpNode> ret = nodeCache.get(oid);
		
		if(null == ret)
		{
			int[] roid = oid.getOid();
			roid = Arrays.copyOf(roid, roid.length);
			Integer index = roid[roid.length-1]; 
			
			Entry<Integer, GetBy1<SnmpNode, SnmpOid>> creEnt = nodeFactories.ceilingEntry(index);
			
			if(null == creEnt)
			{
				LoggingTools.tryLogFormat(LOG, LogLevel.TRACE, "Requesting oid `%s` returns: null", oid);
				//TODO accumulate cache miss
				return null;
			}
			
			int dst = creEnt.getKey();
			
			roid[roid.length-1] = creEnt.getKey();
			SnmpOid actOid = SnmpOid.newInstance(roid);
			Entry<Integer, SnmpNode> add = new KeyVal<>(creEnt.getKey(), creEnt.getValue().getBy(actOid)); 

			//populate intermedaite element, therefor result an instant cache hit.
			for(int i=index;i <= dst;++i)
			{
				roid[roid.length-1] = i;
				nodeCache.put
				(
					SnmpOid.newInstance(Arrays.copyOf(roid, roid.length)),
					add
				);
			}
			
			ret = add;
		}
		
		LoggingTools.tryLogFormat(LOG, LogLevel.TRACE, "Requesting oid `%s` returns: `%s`: `%s`", oid, ret.getKey(), ret.getValue());
		return ret;
	}
	
	@Override
	public boolean hasSubNodes()
	{
		return true;
	}
	
	@Override
	public AttributeAccessor getAccessor()
	{
		return null;
	}
}