/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 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.snmp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class AggregateTracker extends CollectionTracker {
private static class ChildTrackerPduBuilder extends PduBuilder {
private List<SnmpObjId> m_oids = new ArrayList<SnmpObjId>();
private int m_nonRepeaters = 0;
private int m_maxRepititions = 0;
private ResponseProcessor m_responseProcessor;
private int m_nonRepeaterStartIndex;
private int m_repeaterStartIndex;
public ChildTrackerPduBuilder(int maxVarsPerPdu) {
super(maxVarsPerPdu);
}
public void addOid(SnmpObjId snmpObjId) {
m_oids.add(snmpObjId);
}
public void setNonRepeaters(int nonRepeaters) {
m_nonRepeaters = nonRepeaters;
}
public int getNonRepeaters() {
return m_nonRepeaters;
}
public int getRepeaters() {
return size() - getNonRepeaters();
}
public void setMaxRepetitions(int maxRepititions) {
m_maxRepititions = maxRepititions;
}
public int getMaxRepititions() {
return hasRepeaters() ? m_maxRepititions : Integer.MAX_VALUE;
}
public int size() {
return m_oids.size();
}
public void setResponseProcessor(ResponseProcessor responseProcessor) {
m_responseProcessor = responseProcessor;
}
public ResponseProcessor getResponseProcessor() {
return m_responseProcessor;
}
public void addNonRepeaters(PduBuilder pduBuilder) {
for (int i = 0; i < m_nonRepeaters; i++) {
SnmpObjId oid = m_oids.get(i);
pduBuilder.addOid(oid);
}
}
public void addRepeaters(PduBuilder pduBuilder) {
for (int i = m_nonRepeaters; i < m_oids.size(); i++) {
SnmpObjId oid = m_oids.get(i);
pduBuilder.addOid(oid);
}
}
public boolean hasRepeaters() {
return getNonRepeaters() < size();
}
public void setNonRepeaterStartIndex(int nonRepeaterStartIndex) {
m_nonRepeaterStartIndex = nonRepeaterStartIndex;
}
public int getNonRepeaterStartIndex() {
return m_nonRepeaterStartIndex;
}
public void setRepeaterStartIndex(int repeaterStartIndex) {
m_repeaterStartIndex = repeaterStartIndex;
}
public int getRepeaterStartIndex() {
return m_repeaterStartIndex;
}
boolean isNonRepeater(int canonicalIndex) {
return getNonRepeaterStartIndex() <= canonicalIndex && canonicalIndex < getNonRepeaterStartIndex() + getNonRepeaters();
}
boolean isRepeater(int canonicalIndex) {
return getRepeaterStartIndex() <= canonicalIndex && canonicalIndex < getRepeaterStartIndex()+getRepeaters();
}
public int getChildIndex(int canonicalIndex) {
if (isNonRepeater(canonicalIndex)) {
return canonicalIndex - getNonRepeaterStartIndex();
}
if (isRepeater(canonicalIndex)) {
return canonicalIndex - getRepeaterStartIndex() + getNonRepeaters();
}
throw new IllegalArgumentException("index out of range for tracker "+this);
}
}
private class ChildTrackerResponseProcessor implements ResponseProcessor {
private final int m_repeaters;
private final PduBuilder m_pduBuilder;
private final int m_nonRepeaters;
private final List<ChildTrackerPduBuilder> m_childPduBuilders;
private int m_currResponseIndex = 0;
public ChildTrackerResponseProcessor(PduBuilder pduBuilder, List<ChildTrackerPduBuilder> builders, int nonRepeaters, int repeaters) {
m_repeaters = repeaters;
m_pduBuilder = pduBuilder;
m_nonRepeaters = nonRepeaters;
m_childPduBuilders = builders;
}
public void processResponse(SnmpObjId snmpObjId, SnmpValue val) {
ChildTrackerPduBuilder childBuilder = getChildBuilder(m_currResponseIndex++);
childBuilder.getResponseProcessor().processResponse(snmpObjId, val);
}
public boolean processChildError(int errorStatus, int errorIndex) {
int canonicalIndex = getCanonicalIndex(errorIndex-1);
ChildTrackerPduBuilder childBuilder = getChildBuilder(canonicalIndex);
int childIndex = childBuilder.getChildIndex(canonicalIndex);
return childBuilder.getResponseProcessor().processErrors(errorStatus, childIndex+1);
}
private ChildTrackerPduBuilder getChildBuilder(int zeroBasedIndex) {
int canonicalIndex = getCanonicalIndex(zeroBasedIndex);
for (ChildTrackerPduBuilder childBuilder : m_childPduBuilders) {
if (childBuilder.isNonRepeater(canonicalIndex) || childBuilder.isRepeater(canonicalIndex)) {
return childBuilder;
}
}
throw new IllegalStateException("Unable to find childBuilder for index "+zeroBasedIndex);
}
private int getCanonicalIndex(int zeroBasedIndex) {
if (zeroBasedIndex <= 0) {
return 0;
}
if (zeroBasedIndex < m_nonRepeaters) {
return zeroBasedIndex;
}
// return the smallest index of the repeater this index refers to
return ((zeroBasedIndex - m_nonRepeaters) % m_repeaters) + m_nonRepeaters;
}
public boolean processErrors(int errorStatus, int errorIndex) {
if (errorStatus == TOO_BIG_ERR) {
int maxVarsPerPdu = m_pduBuilder.getMaxVarsPerPdu();
if (maxVarsPerPdu <= 1) {
throw new IllegalArgumentException("Unable to handle tooBigError when maxVarsPerPdu = "+maxVarsPerPdu);
}
m_pduBuilder.setMaxVarsPerPdu(maxVarsPerPdu/2);
reportTooBigErr("Reducing maxVarsPerPdu for this request to "+m_pduBuilder.getMaxVarsPerPdu());
return true;
} else if (errorStatus == GEN_ERR) {
return processChildError(errorStatus, errorIndex);
} else if (errorStatus == NO_SUCH_NAME_ERR) {
return processChildError(errorStatus, errorIndex);
} else if (errorStatus != NO_ERR){
throw new IllegalArgumentException("Unrecognized errorStatus "+errorStatus);
} else {
// Continue on.. no need to retry
return false;
}
}
}
private CollectionTracker[] m_children;
public AggregateTracker(Collection<Collectable> children) {
this(children, null);
}
public AggregateTracker(Collection<Collectable> children, CollectionTracker parent) {
this(children.toArray(new Collectable[children.size()]), parent);
}
public AggregateTracker(Collectable[] children) {
this(children, null);
}
public AggregateTracker(Collectable[] children, CollectionTracker parent) {
super(parent);
m_children = new CollectionTracker[children.length];
for (int i = 0; i < m_children.length; i++) {
m_children[i] = children[i].getCollectionTracker();
m_children[i].setParent(this);
}
}
public void setFailed(boolean failed) {
super.setFailed(failed);
for (CollectionTracker child : m_children) {
child.setFailed(failed);
}
}
public void setTimedOut(boolean timedOut) {
super.setTimedOut(timedOut);
for (CollectionTracker child : m_children) {
child.setTimedOut(timedOut);
}
}
@Override
public void setMaxRepetitions(int maxRepititions) {
for (CollectionTracker child : m_children) {
child.setMaxRepetitions(maxRepititions);
}
}
@Override
public boolean isFinished() {
for (CollectionTracker child : m_children) {
if (!child.isFinished()) {
return false;
}
}
return true;
}
public ResponseProcessor buildNextPdu(final PduBuilder parentBuilder) {
// first process the child trackers that aren't finished up to maxVars
int count = 0;
int maxVars = parentBuilder.getMaxVarsPerPdu();
final List<ChildTrackerPduBuilder> builders = new ArrayList<ChildTrackerPduBuilder>(m_children.length);
for (int i = 0; i < m_children.length && count < maxVars; i++) {
CollectionTracker childTracker = m_children[i];
if (!childTracker.isFinished()) {
ChildTrackerPduBuilder childBuilder = new ChildTrackerPduBuilder(maxVars-count);
ResponseProcessor rp = childTracker.buildNextPdu(childBuilder);
childBuilder.setResponseProcessor(rp);
builders.add(childBuilder);
count += childBuilder.size();
}
}
// set the nonRepeaters in the passed in pduBuilder and store indices in the childTrackers
int nonRepeaters = 0;
for (ChildTrackerPduBuilder childBuilder : builders) {
childBuilder.setNonRepeaterStartIndex(nonRepeaters);
childBuilder.addNonRepeaters(parentBuilder);
nonRepeaters += childBuilder.getNonRepeaters();
}
// set the repeaters in the passed in pduBuilder and store indices in the childTrackers
int maxRepititions = Integer.MAX_VALUE;
int repeaters = 0;
for (ChildTrackerPduBuilder childBuilder : builders) {
childBuilder.setRepeaterStartIndex(nonRepeaters+repeaters);
childBuilder.addRepeaters(parentBuilder);
maxRepititions = Math.min(maxRepititions, childBuilder.getMaxRepititions());
repeaters += childBuilder.getRepeaters();
}
// set the non repeaters and max repetitions
parentBuilder.setNonRepeaters(nonRepeaters);
parentBuilder.setMaxRepetitions(maxRepititions == Integer.MAX_VALUE ? 1 : maxRepititions);
// construct a response processor that tracks the changes and informs the response processors
// for the child trackers
return new ChildTrackerResponseProcessor(parentBuilder, builders, nonRepeaters, repeaters);
}
}