| /* |
| * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code 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 |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| |
| package com.sun.jmx.snmp.agent; |
| |
| |
| |
| // java imports |
| // |
| import java.io.Serializable; |
| import java.util.Vector; |
| import java.util.Enumeration; |
| |
| // jmx imports |
| // |
| import com.sun.jmx.snmp.SnmpOid; |
| import com.sun.jmx.snmp.SnmpVarBind; |
| import com.sun.jmx.snmp.SnmpStatusException; |
| |
| /** |
| * Represents a node in an SNMP MIB which is neither a group nor a variable. |
| * This class defines a list of sub-nodes and the methods that allow to |
| * manipulate the sub-nodes. |
| * <P> |
| * This class is used internally and by the class generated by |
| * <CODE>mibgen</CODE>. |
| * You should not need to use this class directly. |
| * |
| * <p><b>This API is a Sun Microsystems internal API and is subject |
| * to change without notice.</b></p> |
| */ |
| |
| public class SnmpMibOid extends SnmpMibNode implements Serializable { |
| private static final long serialVersionUID = 5012254771107446812L; |
| |
| /** |
| * Default constructor. |
| */ |
| public SnmpMibOid() { |
| } |
| |
| // PUBLIC METHODS |
| //--------------- |
| |
| /** |
| * Generic handling of the <CODE>get</CODE> operation. |
| * |
| * <p> This method should be overridden in subclasses. |
| * <p> |
| * |
| * @param req The sub-request that must be handled by this node. |
| * |
| * @param depth The depth reached in the OID tree. |
| * |
| * @exception SnmpStatusException The default implementation (if not |
| * overridden) is to generate a SnmpStatusException. |
| */ |
| @Override |
| public void get(SnmpMibSubRequest req, int depth) |
| throws SnmpStatusException { |
| for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) { |
| SnmpVarBind var= e.nextElement(); |
| SnmpStatusException x = |
| new SnmpStatusException(SnmpStatusException.noSuchObject); |
| req.registerGetException(var,x); |
| } |
| } |
| |
| /** |
| * Generic handling of the <CODE>set</CODE> operation. |
| * |
| * <p> This method should be overridden in subclasses. |
| * <p> |
| * |
| * @param req The sub-request that must be handled by this node. |
| * |
| * @param depth The depth reached in the OID tree. |
| * |
| * @exception SnmpStatusException The default implementation (if not |
| * overridden) is to generate a SnmpStatusException. |
| */ |
| @Override |
| public void set(SnmpMibSubRequest req, int depth) |
| throws SnmpStatusException { |
| for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) { |
| SnmpVarBind var= e.nextElement(); |
| SnmpStatusException x = |
| new SnmpStatusException(SnmpStatusException.noAccess); |
| req.registerSetException(var,x); |
| } |
| } |
| |
| /** |
| * Generic handling of the <CODE>check</CODE> operation. |
| * |
| * <p> This method should be overridden in subclasses. |
| * <p> |
| * |
| * @param req The sub-request that must be handled by this node. |
| * |
| * @param depth The depth reached in the OID tree. |
| * |
| * @exception SnmpStatusException The default implementation (if not |
| * overridden) is to generate a SnmpStatusException. |
| */ |
| @Override |
| public void check(SnmpMibSubRequest req, int depth) |
| throws SnmpStatusException { |
| for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) { |
| SnmpVarBind var= e.nextElement(); |
| SnmpStatusException x = |
| new SnmpStatusException(SnmpStatusException.noAccess); |
| req.registerCheckException(var,x); |
| } |
| } |
| |
| |
| |
| // --------------------------------------------------------------------- |
| // |
| // Implements the method defined in SnmpMibNode. |
| // |
| // --------------------------------------------------------------------- |
| // |
| @Override |
| void findHandlingNode(SnmpVarBind varbind, |
| long[] oid, int depth, |
| SnmpRequestTree handlers) |
| throws SnmpStatusException { |
| |
| |
| final int length = oid.length; |
| SnmpMibNode node = null; |
| |
| if (handlers == null) |
| throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr); |
| |
| if (depth > length) { |
| // Nothing is left... the oid is not valid |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| } else if (depth == length) { |
| // The oid is not complete... |
| throw new SnmpStatusException(SnmpStatusException.noSuchInstance); |
| } else { |
| // Some children variable or subobject is being querried |
| // getChild() will raise an exception if no child is found. |
| // |
| final SnmpMibNode child= getChild(oid[depth]); |
| |
| // XXXX zzzz : what about null children? |
| // (variables for nested groups) |
| // if child==null, then we're dealing with a variable or |
| // a table: we register this node. |
| // This behaviour should be overriden in subclasses, |
| // in particular in group meta classes: the group |
| // meta classes that hold tables should take care |
| // of forwarding this call to all the tables involved. |
| // |
| if (child == null) |
| handlers.add(this,depth,varbind); |
| else |
| child.findHandlingNode(varbind,oid,depth+1,handlers); |
| } |
| } |
| |
| // --------------------------------------------------------------------- |
| // |
| // Implements the method defined in SnmpMibNode. |
| // |
| // --------------------------------------------------------------------- |
| // |
| @Override |
| long[] findNextHandlingNode(SnmpVarBind varbind, |
| long[] oid, int pos, int depth, |
| SnmpRequestTree handlers, |
| AcmChecker checker) |
| throws SnmpStatusException { |
| |
| |
| final int length = oid.length; |
| SnmpMibNode node = null; |
| long[] result = null; |
| if (handlers == null) { |
| // This should be considered as a genErr, but we do not want to |
| // abort the whole request, so we're going to throw |
| // a noSuchObject... |
| // |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| } |
| |
| final Object data = handlers.getUserData(); |
| final int pduVersion = handlers.getRequestPduVersion(); |
| |
| if (pos >= length) { |
| long[] newOid= new long[1]; |
| newOid[0]= getNextVarId(-1,data,pduVersion); |
| result = findNextHandlingNode(varbind,newOid,0,depth,handlers, |
| checker); |
| return result; |
| } |
| |
| // search the element specified in the oid |
| // |
| long[] newOid= new long[1]; |
| long index= oid[pos]; |
| |
| while (true) { |
| |
| try { |
| final SnmpMibNode child = getChild(index); |
| // SnmpOid result = null; |
| if (child == null) { |
| // shouldn't happen |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| // validateVarId(index); |
| // handlers.add(this,varbind,depth); |
| // result = new SnmpOid(0); |
| } else { |
| checker.add(depth, index); |
| try { |
| result = child.findNextHandlingNode(varbind,oid,pos+1, |
| depth+1,handlers, |
| checker); |
| } finally { |
| checker.remove(depth); |
| } |
| } |
| |
| // Build up the leaf OID |
| result[depth] = index; |
| return result; |
| |
| } catch(SnmpStatusException e) { |
| // If there is no such element go one level up ... |
| // |
| index= getNextVarId(index,data,pduVersion); |
| |
| // There is no need to carry the original oid ... |
| newOid[0]=index; |
| pos= 1; |
| oid=newOid; |
| } |
| } |
| } |
| |
| |
| /** |
| * Computes the root OID of the MIB. |
| */ |
| @Override |
| public void getRootOid(Vector<Integer> result) { |
| |
| // If a node has several children, let assume that we are one step to |
| // far in order to get the MIB root. |
| // |
| if (nbChildren != 1) |
| return; |
| |
| result.addElement(varList[0]); |
| |
| // Now query our child. |
| // |
| children.firstElement().getRootOid(result); |
| |
| } |
| |
| /** |
| * Registers a specific node in the tree. |
| */ |
| public void registerNode(String oidString ,SnmpMibNode node) |
| throws IllegalAccessException { |
| SnmpOid oid= new SnmpOid(oidString); |
| registerNode(oid.longValue(), 0, node); |
| } |
| |
| // PROTECTED METHODS |
| //------------------ |
| |
| /** |
| * Registers a specific node in the tree. |
| */ |
| void registerNode(long[] oid, int cursor ,SnmpMibNode node) |
| throws IllegalAccessException { |
| |
| if (cursor >= oid.length) |
| throw new IllegalAccessException(); |
| |
| // Check if the node is already defined |
| // |
| long var= oid[cursor]; |
| |
| //System.out.println("entering registration for val=" |
| // + String.valueOf(var) + " position= " + cursor); |
| |
| int pos = retrieveIndex(var); |
| if (pos == nbChildren) { |
| nbChildren++; |
| varList= new int[nbChildren]; |
| varList[0]= (int) var; |
| pos =0; |
| if ( (cursor + 1) == oid.length) { |
| // That 's the end of the trip. |
| // Do not forward the registration |
| |
| //System.out.println("End of trip for val=" |
| // + String.valueOf(var) + " position= " + cursor); |
| children.insertElementAt(node,pos); |
| return; |
| } |
| |
| //System.out.println("Create node for val=" |
| // + String.valueOf(var) + " position= " + cursor); |
| SnmpMibOid child= new SnmpMibOid(); |
| children.insertElementAt(child, pos); |
| child.registerNode(oid, cursor + 1, node); |
| return; |
| } |
| if (pos == -1) { |
| // The node is not yet registered |
| // |
| int[] tmp= new int[nbChildren + 1]; |
| tmp[nbChildren]= (int) var; |
| System.arraycopy(varList, 0, tmp, 0, nbChildren); |
| varList= tmp; |
| nbChildren++; |
| SnmpMibNode.sort(varList); |
| int newPos = retrieveIndex(var); |
| varList[newPos]= (int) var; |
| if ( (cursor + 1) == oid.length) { |
| // That 's the end of the trip. |
| // Do not forward the registration |
| |
| //System.out.println("End of trip for val=" |
| // + String.valueOf(var) + " position= " + cursor); |
| children.insertElementAt(node, newPos); |
| return; |
| } |
| SnmpMibOid child= new SnmpMibOid(); |
| // System.out.println("Create node for val=" + |
| // String.valueOf(var) + " position= " + cursor); |
| children.insertElementAt(child, newPos); |
| child.registerNode(oid, cursor + 1, node); |
| } |
| else { |
| // The node is already registered |
| // |
| SnmpMibNode child= children.elementAt(pos); |
| if ( (cursor + 1) == oid.length ) { |
| //System.out.println("Node already registered val=" + |
| // String.valueOf(var) + " position= " + cursor); |
| if (child == node) return; |
| if (child != null && node != null) { |
| // Now we're going to patch the tree the following way: |
| // if a subgroup has been registered before its father, |
| // we're going to replace the father OID node with |
| // the actual group-node and export the children from |
| // the temporary OID node to the actual group node. |
| // |
| |
| if (node instanceof SnmpMibGroup) { |
| // `node' is a group => replace `child' with `node' |
| // export the child's subtree to `node'. |
| // |
| ((SnmpMibOid)child).exportChildren((SnmpMibOid)node); |
| children.setElementAt(node,pos); |
| return; |
| |
| } else if ((node instanceof SnmpMibOid) && |
| (child instanceof SnmpMibGroup)) { |
| // `node' is a temporary node, and `child' is a |
| // group => keep child and export the node's |
| // subtree to `child'. |
| // |
| ((SnmpMibOid)node).exportChildren((SnmpMibOid)child); |
| return; |
| } else if (node instanceof SnmpMibOid) { |
| // `node' and `child' are both temporary OID nodes |
| // => replace `child' with `node' and export child's |
| // subtree to `node'. |
| // |
| ((SnmpMibOid)child).exportChildren((SnmpMibOid)node); |
| children.setElementAt(node,pos); |
| return; |
| } |
| } |
| children.setElementAt(node,pos); |
| } else { |
| if (child == null) |
| throw new IllegalAccessException(); |
| ((SnmpMibOid)child).registerNode(oid, cursor + 1, node); |
| } |
| } |
| } |
| |
| /** |
| * Export this node's children to a brother node that will replace |
| * this node in the OID tree. |
| * This method is a patch that fixes the problem of registering |
| * a subnode before its father node. |
| * |
| **/ |
| void exportChildren(SnmpMibOid brother) |
| throws IllegalAccessException { |
| |
| if (brother == null) return; |
| final long[] oid = new long[1]; |
| for (int i=0; i<nbChildren; i++) { |
| final SnmpMibNode child = children.elementAt(i); |
| if (child == null) continue; |
| oid[0] = varList[i]; |
| brother.registerNode(oid,0,child); |
| } |
| } |
| |
| // PRIVATE METHODS |
| //---------------- |
| |
| SnmpMibNode getChild(long id) throws SnmpStatusException { |
| |
| // first we need to retrieve the identifier in the list of children |
| // |
| final int pos= getInsertAt(id); |
| if (pos >= nbChildren) { |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| } |
| |
| if (varList[pos] != (int) id) { |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| } |
| |
| // Access the node |
| // |
| SnmpMibNode child = null; |
| try { |
| child = children.elementAtNonSync(pos); |
| } catch(ArrayIndexOutOfBoundsException e) { |
| throw new SnmpStatusException(SnmpStatusException.noSuchObject); |
| } |
| if (child == null) { |
| throw new SnmpStatusException(SnmpStatusException.noSuchInstance); |
| } |
| return child; |
| } |
| |
| private int retrieveIndex(long val) { |
| |
| int low= 0; |
| int cursor= (int) val; |
| if (varList == null || varList.length < 1) |
| return nbChildren; |
| |
| int max= varList.length -1 ; |
| int curr= low + (max-low)/2; |
| int elmt; |
| while (low <= max) { |
| elmt= varList[curr]; |
| if (cursor == elmt) { |
| // We need to get the next index ... |
| // |
| return curr; |
| } |
| if (elmt < cursor) { |
| low= curr +1; |
| } else { |
| max= curr -1; |
| } |
| curr= low + (max-low)/2; |
| } |
| return -1; |
| } |
| |
| private int getInsertAt(long val) { |
| |
| int low= 0; |
| final int index= (int) val; |
| if (varList == null) |
| return -1; |
| int max= varList.length -1 ; |
| int elmt; |
| //final int[] v = varList; |
| |
| //if (index > a[max]) |
| //return max +1; |
| |
| |
| int curr= low + (max-low)/2; |
| while (low <= max) { |
| |
| elmt= varList[curr]; |
| |
| // never know ...we might find something ... |
| // |
| if (index == elmt) |
| return curr; |
| |
| if (elmt < index) { |
| low= curr +1; |
| } else { |
| max= curr -1; |
| } |
| curr= low + (max-low)/2; |
| } |
| |
| return curr; |
| } |
| |
| // PRIVATE VARIABLES |
| //------------------ |
| |
| /** |
| * Contains the list of sub nodes. |
| */ |
| private NonSyncVector<SnmpMibNode> children = new NonSyncVector<>(1); |
| |
| /** |
| * The number of sub nodes. |
| */ |
| private int nbChildren= 0; |
| |
| |
| // All the methods of the Vector class are synchronized. |
| // Synchronization is a very expensive operation. In our case it is |
| // not always required... |
| // |
| @SuppressWarnings("serial") // We will never serialize this |
| class NonSyncVector<E> extends Vector<E> { |
| |
| public NonSyncVector(int size) { |
| super(size); |
| } |
| |
| final void addNonSyncElement(E obj) { |
| ensureCapacity(elementCount + 1); |
| elementData[elementCount++] = obj; |
| } |
| |
| @SuppressWarnings("unchecked") // cast to E |
| final E elementAtNonSync(int index) { |
| return (E) elementData[index]; |
| } |
| |
| } |
| } |