/* * @(#)file SnmpMib.java * @(#)author Sun Microsystems, Inc. * @(#)version 4.29 * @(#)date 06/05/03 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * */ package com.sun.jmx.snmp.agent; // java imports // import java.io.Serializable; import java.util.Vector; import java.util.Enumeration; import java.lang.IllegalAccessException; // jmx imports // import javax.management.ObjectName; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanRegistrationException; import javax.management.NotCompliantMBeanException; import com.sun.jmx.snmp.SnmpOid; import com.sun.jmx.snmp.SnmpVarBind; import com.sun.jmx.snmp.SnmpDefinitions; import com.sun.jmx.snmp.SnmpStatusException; import com.sun.jmx.snmp.SnmpEngine; import com.sun.jmx.snmp.SnmpUnknownModelException; // SNMP Runtime imports import com.sun.jmx.trace.Trace; import com.sun.jmx.snmp.internal.SnmpAccessControlModel; import com.sun.jmx.snmp.internal.SnmpEngineImpl; /** * This list is used in order to construct the OID during the getnext. * The constructed oid is checked by the checker AcmChecker. */ final class LongList { public static int DEFAULT_CAPACITY = 10; public static int DEFAULT_INCREMENT = 10; private final int DELTA; private int size; /** * The list content. Any access to this variable must be protected * by a synchronized block on the LongList object. * Only read-only action should be performed on this object. **/ public long[] list; LongList() { this(DEFAULT_CAPACITY,DEFAULT_INCREMENT); } LongList(int initialCapacity) { this(initialCapacity,DEFAULT_INCREMENT); } LongList(int initialCapacity, int delta) { size = 0; DELTA = delta; list = allocate(initialCapacity); } /** * Same behaviour than size() in {@link java.util.List}. **/ public final int size() { return size;} /** * Same behaviour than add(long o) in {@link java.util.List}. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final boolean add(final long o) { if (size >= list.length) resize(); list[size++]=o; return true; } /** * Same behaviour than add(int index, long o) in * {@link java.util.List}. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final void add(final int index, final long o) { if (index > size) throw new IndexOutOfBoundsException(); if (index >= list.length) resize(); if (index == size) { list[size++]=o; return; } java.lang.System.arraycopy(list,index,list,index+1,size-index); list[index]=o; size++; } /** * Adds count elements to the list. * @param at index at which the elements must be inserted. The * first element will be inserted at this index. * @param src An array containing the elements we want to insert. * @param from Index of the first element from src that * must be inserted. * @param count number of elements to insert. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final void add(final int at,final long[] src, final int from, final int count) { if (count <= 0) return; if (at > size) throw new IndexOutOfBoundsException(); ensure(size+count); if (at < size) { java.lang.System.arraycopy(list,at,list,at+count,size-at); } java.lang.System.arraycopy(src,from,list,at,count); size+=count; } /** * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final long remove(final int from, final int count) { if (count < 1 || from < 0) return -1; if (from+count > size) return -1; final long o = list[from]; final int oldsize = size; size = size - count; if (from == size) return o; java.lang.System.arraycopy(list,from+count,list,from, size-from); return o; } /** * Same behaviour than remove(int index) in {@link java.util.List}. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final long remove(final int index) { if (index >= size) return -1; final long o = list[index]; list[index]=0; if (index == --size) return o; java.lang.System.arraycopy(list,index+1,list,index, size-index); return o; } /** * Same behaviour than the toArray(long[] a) method in * {@link java.util.List}. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final long[] toArray(long[] a) { java.lang.System.arraycopy(list,0,a,0,size); return a; } /** * Same behaviour than the toArray() method in * {@link java.util.List}. * Any access to this method should be protected in a synchronized * block on the LongList object. **/ public final long[] toArray() { return toArray(new long[size]); } /** * Resize the list. Increase its capacity by DELTA elements. * Any call to this method must be protected by a synchronized * block on this LongList. **/ private final void resize() { final long[] newlist = allocate(list.length + DELTA); java.lang.System.arraycopy(list,0,newlist,0,size); list = newlist; } /** * Resize the list. Insure that the new length will be at * least equal to length. * @param length new minimal length requested. * Any call to this method must be protected by a synchronized * block on this LongList. **/ private final void ensure(int length) { if (list.length < length) { final int min = list.length+DELTA; length=(length * When compiling a SNMP MIB, among all the classes generated by * mibgen, there is one which extends SnmpMib * for representing a whole MIB. *
The class is used by the SNMP protocol adaptor as the entry point in * the MIB. * *

This generated class can be subclassed in your code in order to * plug in your own specific behaviour. *

* *

This API is a Sun Microsystems internal API and is subject * to change without notice.

* @version 4.29 02/25/04 * @author Sun Microsystems, Inc */ public abstract class SnmpMib extends SnmpMibAgent implements Serializable { /** * Default constructor. * Initializes the OID tree. */ public SnmpMib() { root= new SnmpMibOid(); } // -------------------------------------------------------------------- // POLYMORHIC METHODS // -------------------------------------------------------------------- /** *

* This callback should return the OID associated to the group * identified by the given groupName. *

* *

* This method is provided as a hook to plug-in some custom * specific behavior. Although doing so is discouraged you might * want to subclass this method in order to store & provide more metadata * information (mapping OID <-> symbolic name) within the agent, * or to "change" the root of the MIB OID by prefixing the * defaultOid by an application dependant OID string, for instance. *

* *

* The default implementation of this method is to return the given * defaultOid *

* * @param groupName The java-ized name of the SNMP group. * @param defaultOid The OID defined in the MIB for that group * (in dot notation). * * @return The OID of the group identified by groupName, * in dot-notation. */ protected String getGroupOid(String groupName, String defaultOid) { return defaultOid; } /** *

* This callback should return the ObjectName associated to the * group identified by the given groupName. *

* *

* This method is provided as a hook to plug-in some custom * specific behavior. You might want to override this method * in order to provide a different object naming scheme than * that proposed by default by mibgen. *

* *

* This method is only meaningful if the MIB is registered * in the MBeanServer, otherwise, it will not be called. *

* *

* The default implementation of this method is to return an ObjectName * built from the given defaultName. *

* * @param name The java-ized name of the SNMP group. * @param oid The OID returned by getGroupOid() - in dot notation. * @param defaultName The name by default generated by * mibgen * * @return The ObjectName of the group identified by name */ protected ObjectName getGroupObjectName(String name, String oid, String defaultName) throws MalformedObjectNameException { return new ObjectName(defaultName); } /** *

* Register an SNMP group and its metadata node in the MIB. *

* *

* This method is provided as a hook to plug-in some custom * specific behavior. You might want to override this method * if you want to set special links between the MBean, its metadata * node, its OID or ObjectName etc.. *

* *

* If the MIB is not registered in the MBeanServer, the * server and groupObjName parameters will be * null.
* If the given group MBean is not null, and if the * server and groupObjName parameters are * not null, then this method will also automatically register the * group MBean with the given MBeanServer server. *

* * @param groupName The java-ized name of the SNMP group. * @param groupOid The OID as returned by getGroupOid() - in dot * notation. * @param groupObjName The ObjectName as returned by getGroupObjectName(). * This parameter may be null if the * MIB is not registered in the MBeanServer. * @param node The metadata node, as returned by the metadata * factory method for this group. * @param group The MBean for this group, as returned by the * MBean factory method for this group. * @param server The MBeanServer in which the groups are to be * registered. This parameter will be null * if the MIB is not registered, otherwise it is a * reference to the MBeanServer in which the MIB is * registered. * */ protected void registerGroupNode(String groupName, String groupOid, ObjectName groupObjName, SnmpMibNode node, Object group, MBeanServer server) throws NotCompliantMBeanException, MBeanRegistrationException, InstanceAlreadyExistsException, IllegalAccessException { root.registerNode(groupOid,node); if (server != null && groupObjName != null && group != null) server.registerMBean(group,groupObjName); } /** *

* Register an SNMP Table metadata node in the MIB. *

* *

* * This method is used internally and you should never need to * call it directly.
It is used to establish the link * between an SNMP table metadata node and its bean-like counterpart. *
* The group metadata nodes will create and register their * underlying table metadata nodes in the MIB using this * method.
* The metadata nodes will be later retrieved from the MIB by the * bean-like table objects using the getRegisterTableMeta() method. *

* * @param name The java-ized name of the SNMP table. * @param table The SNMP table metadata node - usually this * corresponds to a mibgen generated * object. */ public abstract void registerTableMeta(String name, SnmpMibTable table); /** * Returns a registered SNMP Table metadata node. * *

* This method is used internally and you should never need to * call it directly. *

* */ public abstract SnmpMibTable getRegisteredTableMeta(String name); // -------------------------------------------------------------------- // PUBLIC METHODS // -------------------------------------------------------------------- /** * Processes a get operation. * **/ // Implements the method defined in SnmpMibAgent. See SnmpMibAgent // for java-doc // public void get(SnmpMibRequest req) throws SnmpStatusException { // Builds the request tree: creation is not allowed, operation // is not atomic. final int reqType = SnmpDefinitions.pduGetRequestPdu; SnmpRequestTree handlers = getHandlers(req,false,false,reqType); SnmpRequestTree.Handler h = null; SnmpMibNode meta = null; if (isDebugOn()) debug("get","Processing handlers for GET... "); // For each sub-request stored in the request-tree, invoke the // get() method. for (Enumeration eh=handlers.getHandlers();eh.hasMoreElements();) { h = (SnmpRequestTree.Handler) eh.nextElement(); // Gets the Meta node. It can be either a Group Meta or a // Table Meta. // meta = handlers.getMetaNode(h); // Gets the depth of the Meta node in the OID tree final int depth = handlers.getOidDepth(h); for (Enumeration rqs=handlers.getSubRequests(h); rqs.hasMoreElements();) { // Invoke the get() operation. meta.get((SnmpMibSubRequest)rqs.nextElement(),depth); } } } /** * Processes a set operation. * */ // Implements the method defined in SnmpMibAgent. See SnmpMibAgent // for java-doc // public void set(SnmpMibRequest req) throws SnmpStatusException { SnmpRequestTree handlers = null; // Optimization: we're going to get the whole SnmpRequestTree // built in the "check" method, so that we don't have to rebuild // it here. // if (req instanceof SnmpMibRequestImpl) handlers = ((SnmpMibRequestImpl)req).getRequestTree(); // Optimization didn't work: we have to rebuild the tree. // // Builds the request tree: creation is not allowed, operation // is atomic. // final int reqType = SnmpDefinitions.pduSetRequestPdu; if (handlers == null) handlers = getHandlers(req,false,true,reqType); handlers.switchCreationFlag(false); handlers.setPduType(reqType); SnmpRequestTree.Handler h = null; SnmpMibNode meta = null; if (isDebugOn()) debug("set","Processing handlers for SET... "); // For each sub-request stored in the request-tree, invoke the // get() method. for (Enumeration eh=handlers.getHandlers();eh.hasMoreElements();) { h = (SnmpRequestTree.Handler) eh.nextElement(); // Gets the Meta node. It can be either a Group Meta or a // Table Meta. // meta = handlers.getMetaNode(h); // Gets the depth of the Meta node in the OID tree final int depth = handlers.getOidDepth(h); for (Enumeration rqs=handlers.getSubRequests(h); rqs.hasMoreElements();) { // Invoke the set() operation meta.set((SnmpMibSubRequest)rqs.nextElement(),depth); } } } /** * Checks if a set operation can be performed. * If the operation cannot be performed, the method will raise a * SnmpStatusException. * */ // Implements the method defined in SnmpMibAgent. See SnmpMibAgent // for java-doc // public void check(SnmpMibRequest req) throws SnmpStatusException { final int reqType = SnmpDefinitions.pduWalkRequest; // Builds the request tree: creation is allowed, operation // is atomic. SnmpRequestTree handlers = getHandlers(req,true,true,reqType); SnmpRequestTree.Handler h = null; SnmpMibNode meta = null; if (isDebugOn()) debug("check","Processing handlers for CHECK... "); // For each sub-request stored in the request-tree, invoke the // check() method. for (Enumeration eh=handlers.getHandlers();eh.hasMoreElements();) { h = (SnmpRequestTree.Handler) eh.nextElement(); // Gets the Meta node. It can be either a Group Meta or a // Table Meta. // meta = handlers.getMetaNode(h); // Gets the depth of the Meta node in the OID tree final int depth = handlers.getOidDepth(h); for (Enumeration rqs=handlers.getSubRequests(h); rqs.hasMoreElements();) { // Invoke the check() operation meta.check((SnmpMibSubRequest)rqs.nextElement(),depth); } } // Optimization: we're going to pass the whole SnmpRequestTree // to the "set" method, so that we don't have to rebuild it there. // if (req instanceof SnmpMibRequestImpl) { ((SnmpMibRequestImpl)req).setRequestTree(handlers); } } /** * Processes a getNext operation. * */ // Implements the method defined in SnmpMibAgent. See SnmpMibAgent // for java-doc // public void getNext(SnmpMibRequest req) throws SnmpStatusException { // Build the request tree for the operation // The subrequest stored in the request tree are valid GET requests SnmpRequestTree handlers = getGetNextHandlers(req); SnmpRequestTree.Handler h = null; SnmpMibNode meta = null; if (isDebugOn()) debug("getNext","Processing handlers for GET-NEXT... "); // Now invoke get() for each subrequest of the request tree. for (Enumeration eh=handlers.getHandlers();eh.hasMoreElements();) { h = (SnmpRequestTree.Handler) eh.nextElement(); // Gets the Meta node. It can be either a Group Meta or a // Table Meta. // meta = handlers.getMetaNode(h); // Gets the depth of the Meta node in the OID tree int depth = handlers.getOidDepth(h); for (Enumeration rqs=handlers.getSubRequests(h); rqs.hasMoreElements();) { // Invoke the get() operation meta.get((SnmpMibSubRequest)rqs.nextElement(),depth); } } } /** * Processes a getBulk operation. * The method implements the getBulk operation by calling * appropriately the getNext method. * */ // Implements the method defined in SnmpMibAgent. See SnmpMibAgent // for java-doc // public void getBulk(SnmpMibRequest req, int nonRepeat, int maxRepeat) throws SnmpStatusException { getBulkWithGetNext(req, nonRepeat, maxRepeat); } /** * Gets the root object identifier of the MIB. *

In order to be accurate, the method should be called once the * MIB is fully initialized (that is, after a call to init * or preRegister). * * @return The root object identifier. */ public long[] getRootOid() { if( rootOid == null) { Vector list= new Vector(10); // Ask the tree to do the job ! // root.getRootOid(list); // Now format the result // rootOid= new long[list.size()]; int i=0; for(Enumeration e= list.elements(); e.hasMoreElements(); ) { Integer val= (Integer) e.nextElement(); rootOid[i++]= val.longValue(); } } return rootOid; } // -------------------------------------------------------------------- // PRIVATE METHODS //--------------------------------------------------------------------- /** * This method builds the temporary request-tree that will be used to * perform the SNMP request associated with the given vector of varbinds * `list'. * * @param req The SnmpMibRequest object holding the varbind list * concerning this MIB. * @param createflag Indicates whether the operation allow for creation * of new instances (ie: it is a SET). * @param atomic Indicates whether the operation is atomic or not. * @param type Request type (from SnmpDefinitions). * * @return The request-tree where the original varbind list has been * dispatched to the appropriate nodes. */ private SnmpRequestTree getHandlers(SnmpMibRequest req, boolean createflag, boolean atomic, int type) throws SnmpStatusException { // Build an empty request tree SnmpRequestTree handlers = new SnmpRequestTree(req,createflag,type); int index=0; SnmpVarBind var = null; final int ver= req.getVersion(); // For each varbind in the list finds its handling node. for (Enumeration e= req.getElements(); e.hasMoreElements(); index++) { var= (SnmpVarBind) e.nextElement(); try { // Find the handling node for this varbind. root.findHandlingNode(var,var.oid.longValue(false), 0,handlers); } catch(SnmpStatusException x) { if (isDebugOn()) debug("getHandlers","Couldn't find a handling node for " + var.oid.toString()); // If the operation is atomic (Check/Set) or the version // is V1 we must generate an exception. // if (ver == SnmpDefinitions.snmpVersionOne) { if (isDebugOn()) debug("getHandlers","\tV1: Throwing exception"); // The index in the exception must correspond to the // SNMP index ... // final SnmpStatusException sse = new SnmpStatusException(x, index + 1); sse.initCause(x); throw sse; } else if ((type == SnmpDefinitions.pduWalkRequest) || (type == SnmpDefinitions.pduSetRequestPdu)) { final int status = SnmpRequestTree.mapSetException(x.getStatus(),ver); if (isDebugOn()) debug("getHandlers","\tSET: Throwing exception"); final SnmpStatusException sse = new SnmpStatusException(status, index + 1); sse.initCause(x); throw sse; } else if (atomic) { // Should never come here... if (isDebugOn()) debug("getHandlers","\tATOMIC: Throwing exception"); final SnmpStatusException sse = new SnmpStatusException(x, index + 1); sse.initCause(x); throw sse; } final int status = SnmpRequestTree.mapGetException(x.getStatus(),ver); if (status == SnmpStatusException.noSuchInstance) { if (isDebugOn()) debug("getHandlers", "\tGET: Registering noSuchInstance"); var.value= SnmpVarBind.noSuchInstance; } else if (status == SnmpStatusException.noSuchObject) { if (isDebugOn()) debug("getHandlers", "\tGET: Registering noSuchObject"); var.value= SnmpVarBind.noSuchObject; } else { if (isDebugOn()) debug("getHandlers", "\tGET: Registering global error: " + status); final SnmpStatusException sse = new SnmpStatusException(status, index + 1); sse.initCause(x); throw sse; } } } return handlers; } /** * This method builds the temporary request-tree that will be used to * perform the SNMP GET-NEXT request associated with the given vector * of varbinds `list'. * * @param req The SnmpMibRequest object holding the varbind list * concerning this MIB. * * @return The request-tree where the original varbind list has been * dispatched to the appropriate nodes, and where the original * OIDs have been replaced with the correct "next" OID. */ private SnmpRequestTree getGetNextHandlers(SnmpMibRequest req) throws SnmpStatusException { // Creates an empty request tree, no entry creation is allowed (false) SnmpRequestTree handlers = new SnmpRequestTree(req,false,SnmpDefinitions.pduGetNextRequestPdu); // Sets the getNext flag: if version=V2, status exception are // transformed in endOfMibView handlers.setGetNextFlag(); if (isDebugOn()) debug("getGetNextHandlers","Received MIB request : " + req); AcmChecker checker = new AcmChecker(req); int index=0; SnmpVarBind var = null; final int ver= req.getVersion(); SnmpOid original = null; // For each varbind, finds the handling node. // This function has the side effect of transforming a GET-NEXT // request into a valid GET request, replacing the OIDs in the // original GET-NEXT request with the OID of the first leaf that // follows. for (Enumeration e= req.getElements(); e.hasMoreElements(); index++) { var = (SnmpVarBind) e.nextElement(); SnmpOid result = null; try { // Find the node handling the OID that follows the varbind // OID. `result' contains this next leaf OID. //ACM loop. if (isDebugOn()) debug("getGetNextHandlers"," Next Oid of :" + var.oid); result = new SnmpOid(root.findNextHandlingNode (var,var.oid.longValue(false),0, 0,handlers, checker)); if (isDebugOn()) debug("getGetNextHandlers"," is :" + result); // We replace the varbind original OID with the OID of the // leaf object we have to return. var.oid = result; } catch(SnmpStatusException x) { // if (isDebugOn()) // debug("getGetNextHandlers", // "Couldn't find a handling node for " // + var.oid.toString()); if (ver == SnmpDefinitions.snmpVersionOne) { if (isDebugOn()) debug("getGetNextHandlers","\tThrowing exception" + x.toString()); // The index in the exception must correspond to the // SNMP index ... // throw new SnmpStatusException(x, index + 1); } if (isDebugOn()) debug("getGetNextHandlers","Exception : " + x.getStatus()); var.setSnmpValue(SnmpVarBind.endOfMibView); } } return handlers; } // Returns true if debug is on private final static boolean isDebugOn() { return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP); } // Prints a debug message private final static void debug(String func, String info) { Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP, "SnmpMib", func, info); } // -------------------------------------------------------------------- // PROTECTED VARIABLES // -------------------------------------------------------------------- /** * The top element in the Mib tree. * @serial */ protected SnmpMibOid root; // -------------------------------------------------------------------- // PRIVATE VARIABLES // -------------------------------------------------------------------- /** * The root object identifier of the MIB. */ private transient long[] rootOid= null; }