/*_##########################################################################
_##
_## Copyright (C) 2011-2012 Kaito Yamada
_##
_##########################################################################
*/
package com.github.kaitoy.sneo.agent.mo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.snmp4j.agent.DefaultMOScope;
import org.snmp4j.agent.DuplicateRegistrationException;
import org.snmp4j.agent.MOGroup;
import org.snmp4j.agent.MOScope;
import org.snmp4j.agent.MOServer;
import org.snmp4j.agent.ManagedObject;
import org.snmp4j.agent.request.RequestStatus;
import org.snmp4j.agent.request.SubRequest;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.Variable;
import org.snmp4j.smi.VariableBinding;
/**
* The <code>StaticMutableMOGroup</code> can be used to easily implement static
* (writable) managed objects.
* <p>
* Note: Dynamic variables (see {@link Variable#isDynamic}) cannot be used to
* when using default {@link VariableBinding}s since {@link Variable}s are
* cloned when added to them. In order to use dynamic objects (i.e., objects
* that may change their value when being accessed), a sub-class of
* {@link VariableBinding} needs to be used that overwrites its
* {@link VariableBinding#setVariable} method.
* </p>
*/
public class MutableStaticMOGroup implements ManagedObject, MOGroup {
// private static final LogAdapter logger
// = LogFactory.getLogger(MutableStaticMOGroup.class);
private final OID root;
private final MOScope scope;
private final SortedMap<OID, Variable> variableBindings;
private final Map<OID, VariableServer> varServerRegistry
= new HashMap<OID, VariableServer>();
private final SnmpAccessStatisticsGatherer snmpAccessStatisticsGatherer
= new SnmpAccessStatisticsGathererImpl();
private boolean enableSnmpAccessStatisticsGatherer = false;
/**
* Creates a static managed object group for the sub-tree with the specified
* root OID.
* @param root
* the root OID of the sub-tree to be registered by this managed object.
* @param vbs
* the variable bindings to be returned in this sub-tree.
*/
public MutableStaticMOGroup(OID root, VariableBinding[] vbs) {
this.root = root;
this.scope = new DefaultMOScope(root, true, root.nextPeer(), false);
variableBindings = new TreeMap<OID, Variable>();
for (int i = 0; i < vbs.length; i++) {
if ((vbs[i].getOid() != null) && (vbs[i].getVariable() != null)) {
if (
(vbs[i].getOid().size() >= root.size())
&& (vbs[i].getOid().leftMostCompare(root.size(), root) == 0)
) {
this.variableBindings.put(vbs[i].getOid(), vbs[i].getVariable());
}
}
}
}
private MutableStaticMOGroup(
OID root, SortedMap<OID, Variable> variableBindings,
MOScope scope
) {
this.root = root;
this.variableBindings = variableBindings;
this.scope = scope;
}
public OID getRoot() {
return root;
}
public MOScope getScope() {
return scope;
}
public
SnmpAccessStatisticsGatherer getSnmpAccessStatisticsGatherer() {
return snmpAccessStatisticsGatherer;
}
public void setEnableSnmpAccessStatisticsGatherer(
boolean enableSnmpAccessStatisticsGatherer
) {
this.enableSnmpAccessStatisticsGatherer
= enableSnmpAccessStatisticsGatherer;
if (!enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer.clear();
}
}
public boolean isEnableSnmpAccessStatisticsGatherer() {
return enableSnmpAccessStatisticsGatherer;
}
public void put(OID oid, Variable var) {
synchronized (variableBindings) {
variableBindings.put(oid, var);
}
}
public void putAll(Map<OID, Variable> vbs) {
synchronized (variableBindings) {
variableBindings.putAll(vbs);
}
}
public void putAll(List<VariableBinding> vbs) {
Map<OID, Variable> vbsMap = new HashMap<OID, Variable>();
for (VariableBinding vb: vbs) {
vbsMap.put(vb.getOid(), vb.getVariable());
}
synchronized (variableBindings) {
variableBindings.putAll(vbsMap);
}
}
public void registerMOs(
MOServer server, OctetString context
) throws DuplicateRegistrationException {
server.register(this, context);
}
public void unregisterMOs(MOServer server, OctetString context) {
server.unregister(this, context);
}
public void registerVariableServer(OID oid, VariableServer vs) {
synchronized (varServerRegistry) {
varServerRegistry.put(oid, vs);
}
}
public int size() {
synchronized (variableBindings) {
return variableBindings.size();
}
}
public OID find(MOScope range) {
OID targetOid = range.getLowerBound();
SortedMap<OID, Variable> tail;
if (range.isLowerIncluded()) {
synchronized (variableBindings) {
tail = variableBindings.tailMap(targetOid);
}
}
else {
synchronized (variableBindings) {
tail = variableBindings.tailMap(targetOid.successor());
}
}
if (tail.size() == 0) {
return null;
}
return tail.firstKey();
}
public void get(SubRequest request) {
OID oid = request.getVariableBinding().getOid();
Variable var = get(oid);
if (var == null) {
request.getVariableBinding().setVariable(Null.noSuchInstance);
request.completed();
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer
.snmpGetFailed(request.getRequest().getContext(), oid);
}
return;
}
request.getVariableBinding().setVariable(var);
request.completed();
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer
.snmpGetSucceeded(request.getRequest().getContext(), oid);
}
}
public boolean next(SubRequest request) {
// if (!request.getScope().equals(request.getQuery().getScope())) {
// System.out.println("Never get here.");
// }
MOScope scope = request.getScope();
OID requestedOid = scope.getLowerBound();
OID nextOid = find(scope);
if (nextOid == null) {
request.getVariableBinding().setVariable(Null.noSuchInstance);
request.completed();
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer
.snmpNextFailed(request.getRequest().getContext(), requestedOid);
}
return false;
}
request.getVariableBinding().setOid(nextOid);
request.getVariableBinding().setVariable(get(nextOid));
request.completed();
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer
.snmpNextSucceeded(request.getRequest().getContext(), requestedOid);
}
return true;
}
public Variable get(OID oid) {
synchronized (varServerRegistry) {
if (varServerRegistry.containsKey(oid)) {
return varServerRegistry.get(oid).get();
}
}
synchronized (variableBindings) {
return variableBindings.get(oid);
}
}
private VariableBinding next(OID oid) {
SortedMap<OID, Variable> tail;
synchronized (variableBindings) {
tail = variableBindings.tailMap(oid.successor());
}
if (tail.size() == 0) {
return null;
}
OID first = tail.firstKey();
return new VariableBinding(first, get(first));
}
public List<VariableBinding> walk(OID oid, int count) {
List<VariableBinding> varbinds = new ArrayList<VariableBinding>();
OID nextTarget = oid;
for (int i = 0; i < count; i++) {
VariableBinding vb = next(nextTarget);
if (vb == null || !vb.getOid().startsWith(oid)) {
return varbinds;
}
varbinds.add(vb);
nextTarget = vb.getOid();
}
return varbinds;
}
public Variable set(OID oid, Variable var) {
synchronized (variableBindings) {
return variableBindings.put(oid, var);
}
}
/**
* Checks whether the new value contained in the supplied sub-request is a
* valid value for this object. The checks are performed by firing a
* {@link MOValueValidationEvent} the registered listeners.
*
* @param request
* the <code>SubRequest</code> with the new value.
* @return
* {@link SnmpConstants#SNMP_ERROR_SUCCESS} if the new value is OK,
* any other appropriate SNMPv2/v3 error status if not.
*/
// private int isValueOK(SubRequest request) {
// Variable var = request.getVariableBinding().getVariable();
//
// switch (var.getSyntax()) {
// case SMIConstants.SYNTAX_INTEGER : // == INTEGER32
// break;
// case SMIConstants.SYNTAX_OCTET_STRING :
// break;
// case SMIConstants.SYNTAX_OBJECT_IDENTIFIER :
// break;
// case SMIConstants.SYNTAX_IPADDRESS :
// break;
// case SMIConstants.SYNTAX_COUNTER32 : // == GAUGE32
// case SMIConstants.SYNTAX_UNSIGNED_INTEGER32 :
// case SMIConstants.SYNTAX_TIMETICKS :
// break;
// case SMIConstants.SYNTAX_OPAQUE :
// break;
// case SMIConstants.SYNTAX_COUNTER64 :
// break;
// default :
// return SnmpConstants.SNMP_ERROR_WRONG_TYPE;
// }
//
// return SnmpConstants.SNMP_ERROR_SUCCESS;
// }
/**
* Prepare
* @param request
* a request to process prepare SET request for.
*/
public void prepare(SubRequest request) {
RequestStatus status = request.getStatus();
MOScope scp = request.getQuery().getScope();
Variable variable = request.getVariableBinding().getVariable();
OID oid = request.getVariableBinding().getOid();
if (!scope.isCovered(scp)) {
status.setErrorStatus(SnmpConstants.SNMP_ERROR_NO_CREATION);
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer.snmpSetFailed(request.getRequest().getContext(), oid);
}
return;
}
synchronized (variableBindings) {
if (variableBindings.containsKey(oid)) {
if (
variable.getSyntax()
!= variableBindings.get(oid).getSyntax()
) {
status.setErrorStatus(SnmpConstants.SNMP_ERROR_WRONG_TYPE);
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer.snmpSetFailed(request.getRequest().getContext(), oid);
}
return;
}
//int valueOK = isValueOK(request);
//status.setErrorStatus(valueOK);
status.setErrorStatus(SnmpConstants.SNMP_ERROR_SUCCESS);
request.completed();
}
else {
status.setErrorStatus(SnmpConstants.SNMP_ERROR_SUCCESS);
request.completed();
}
}
}
/**
* commit
* @param request
* a request to process commit SET request for.
*/
public void commit(SubRequest request) {
Variable variable = request.getVariableBinding().getVariable();
OID oid = request.getVariableBinding().getOid();
synchronized (variableBindings) {
request.setUndoValue(variableBindings.get(oid));
variableBindings.put(oid, variable);
}
if (enableSnmpAccessStatisticsGatherer) {
snmpAccessStatisticsGatherer.snmpSetSucceeded(request.getRequest().getContext(), oid);
}
request.completed();
}
/**
* undo
* @param request
* a request to process undo SET request for.
*/
public void undo(SubRequest request) {
RequestStatus status = request.getStatus();
if (
(request.getUndoValue() != null)
&& (request.getUndoValue() instanceof Variable)
) {
synchronized (variableBindings) {
variableBindings.put(
request.getVariableBinding().getOid(),
(Variable)request.getUndoValue()
);
}
status.setErrorStatus(SnmpConstants.SNMP_ERROR_SUCCESS);
request.completed();
}
else {
synchronized (variableBindings) {
variableBindings.remove(request.getVariableBinding().getOid());
}
status.setErrorStatus(SnmpConstants.SNMP_ERROR_SUCCESS);
request.completed();
}
// TODO mibAccessStatisticsă‚‚undo?
}
public void cleanup(SubRequest request) {
request.setUndoValue(null);
request.getStatus().setPhaseComplete(true);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName());
sb.append("[root=");
sb.append(root);
sb.append(",subtee count=");
sb.append(variableBindings.size());
sb.append("]");
return sb.toString();
}
public MutableStaticMOGroup shallowCopy() {
SortedMap<OID, Variable> vbsCopy
= new TreeMap<OID, Variable>(variableBindings);
// TODO should clone scope too.
return new MutableStaticMOGroup(new OID(root), vbsCopy, scope);
}
public MutableStaticMOGroup shallowCopy(List<OID> excludes) {
if (excludes == null) {
throw new NullPointerException("excludes is null");
}
if (excludes.isEmpty()) {
return shallowCopy();
}
excludes = new ArrayList<OID>(excludes);
Collections.sort(excludes);
Map<OID, Variable> vbsCopy = new HashMap<OID, Variable>();
OID last = null;
try {
for (OID oid: excludes) {
if (last == null) {
vbsCopy.putAll(variableBindings.headMap(oid));
}
else {
vbsCopy.putAll(variableBindings.subMap(last.nextPeer(), oid));
}
last = oid;
}
vbsCopy.putAll(variableBindings.tailMap(last.nextPeer()));
} catch (IllegalArgumentException e) {
throw e; // TODO
}
// TODO should clone scope too.
return new MutableStaticMOGroup(new OID(root), new TreeMap<OID, Variable>(vbsCopy), scope);
}
}