1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 package com.sun.jmx.snmp.agent;
  28 
  29 
  30 
  31 // java imports
  32 //
  33 import java.io.Serializable;
  34 import java.util.Vector;
  35 import java.util.Enumeration;
  36 
  37 // jmx imports
  38 //
  39 import com.sun.jmx.snmp.SnmpOid;
  40 import com.sun.jmx.snmp.SnmpVarBind;
  41 import com.sun.jmx.snmp.SnmpStatusException;
  42 
  43 /**
  44  * Represents a node in an SNMP MIB which is neither a group nor a variable.
  45  * This class defines a list of sub-nodes and the methods that allow to
  46  * manipulate the sub-nodes.
  47  * <P>
  48  * This class is used internally and by the class generated by
  49  * <CODE>mibgen</CODE>.
  50  * You should not need to use this class directly.
  51  *
  52  * <p><b>This API is a Sun Microsystems internal API  and is subject
  53  * to change without notice.</b></p>
  54  */
  55 
  56 public class SnmpMibOid extends SnmpMibNode implements Serializable {
  57     private static final long serialVersionUID = 5012254771107446812L;
  58 
  59     /**
  60      * Default constructor.
  61      */
  62     public SnmpMibOid() {
  63     }
  64 
  65     // PUBLIC METHODS
  66     //---------------
  67 
  68     /**
  69      * Generic handling of the <CODE>get</CODE> operation.
  70      *
  71      * <p> This method should be overridden in subclasses.
  72      * <p>
  73      *
  74      * @param req   The sub-request that must be handled by this node.
  75      *
  76      * @param depth The depth reached in the OID tree.
  77      *
  78      * @exception SnmpStatusException The default implementation (if not
  79      *            overridden) is to generate a SnmpStatusException.
  80      */
  81     @Override
  82     public void get(SnmpMibSubRequest req, int depth)
  83         throws SnmpStatusException {
  84         for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) {
  85             SnmpVarBind var= e.nextElement();
  86             SnmpStatusException x =
  87                 new SnmpStatusException(SnmpStatusException.noSuchObject);
  88             req.registerGetException(var,x);
  89         }
  90     }
  91 
  92     /**
  93      * Generic handling of the <CODE>set</CODE> operation.
  94      *
  95      * <p> This method should be overridden in subclasses.
  96      * <p>
  97      *
  98      * @param req   The sub-request that must be handled by this node.
  99      *
 100      * @param depth The depth reached in the OID tree.
 101      *
 102      * @exception SnmpStatusException The default implementation (if not
 103      *            overridden) is to generate a SnmpStatusException.
 104      */
 105     @Override
 106     public void set(SnmpMibSubRequest req, int depth)
 107         throws SnmpStatusException {
 108         for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) {
 109             SnmpVarBind var= e.nextElement();
 110             SnmpStatusException x =
 111                 new SnmpStatusException(SnmpStatusException.noAccess);
 112             req.registerSetException(var,x);
 113         }
 114     }
 115 
 116     /**
 117      * Generic handling of the <CODE>check</CODE> operation.
 118      *
 119      * <p> This method should be overridden in subclasses.
 120      * <p>
 121      *
 122      * @param req   The sub-request that must be handled by this node.
 123      *
 124      * @param depth The depth reached in the OID tree.
 125      *
 126      * @exception SnmpStatusException The default implementation (if not
 127      *            overridden) is to generate a SnmpStatusException.
 128      */
 129     @Override
 130     public void check(SnmpMibSubRequest req, int depth)
 131         throws SnmpStatusException {
 132         for (Enumeration<SnmpVarBind> e= req.getElements(); e.hasMoreElements();) {
 133             SnmpVarBind var= e.nextElement();
 134             SnmpStatusException x =
 135                 new SnmpStatusException(SnmpStatusException.noAccess);
 136             req.registerCheckException(var,x);
 137         }
 138     }
 139 
 140 
 141 
 142     // ---------------------------------------------------------------------
 143     //
 144     // Implements the method defined in SnmpMibNode.
 145     //
 146     // ---------------------------------------------------------------------
 147     //
 148     @Override
 149     void findHandlingNode(SnmpVarBind varbind,
 150                           long[] oid, int depth,
 151                           SnmpRequestTree handlers)
 152         throws SnmpStatusException {
 153 
 154 
 155         final int length = oid.length;
 156         SnmpMibNode node = null;
 157 
 158         if (handlers == null)
 159             throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
 160 
 161         if (depth > length) {
 162             // Nothing is left... the oid is not valid
 163             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 164         } else if (depth == length) {
 165             // The oid is not complete...
 166             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 167         } else {
 168             // Some children variable or subobject is being querried
 169             // getChild() will raise an exception if no child is found.
 170             //
 171             final SnmpMibNode child= getChild(oid[depth]);
 172 
 173             // XXXX zzzz : what about null children?
 174             //             (variables for nested groups)
 175             // if child==null, then we're dealing with a variable or
 176             // a table: we register this node.
 177             // This behaviour should be overriden in subclasses,
 178             // in particular in group meta classes: the group
 179             // meta classes that hold tables should take care
 180             // of forwarding this call to all the tables involved.
 181             //
 182             if (child == null)
 183                 handlers.add(this,depth,varbind);
 184             else
 185                 child.findHandlingNode(varbind,oid,depth+1,handlers);
 186         }
 187     }
 188 
 189     // ---------------------------------------------------------------------
 190     //
 191     // Implements the method defined in SnmpMibNode.
 192     //
 193     // ---------------------------------------------------------------------
 194     //
 195     @Override
 196     long[] findNextHandlingNode(SnmpVarBind varbind,
 197                                 long[] oid, int pos, int depth,
 198                                 SnmpRequestTree handlers,
 199                                 AcmChecker checker)
 200         throws SnmpStatusException {
 201 
 202 
 203         final int length = oid.length;
 204         SnmpMibNode node = null;
 205         long[] result = null;
 206         if (handlers == null) {
 207             // This should be considered as a genErr, but we do not want to
 208             // abort the whole request, so we're going to throw
 209             // a noSuchObject...
 210             //
 211             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 212         }
 213 
 214         final Object data = handlers.getUserData();
 215         final int pduVersion = handlers.getRequestPduVersion();
 216 
 217         if (pos >= length) {
 218             long[] newOid= new long[1];
 219             newOid[0]=  getNextVarId(-1,data,pduVersion);
 220             result = findNextHandlingNode(varbind,newOid,0,depth,handlers,
 221                                           checker);
 222             return result;
 223         }
 224 
 225         // search the element specified in the oid
 226         //
 227         long[] newOid= new long[1];
 228         long index= oid[pos];
 229 
 230         while (true) {
 231 
 232             try {
 233                 final SnmpMibNode child = getChild(index);
 234                 // SnmpOid result = null;
 235                 if (child == null) {
 236                     // shouldn't happen
 237                     throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 238                     // validateVarId(index);
 239                     // handlers.add(this,varbind,depth);
 240                     // result = new SnmpOid(0);
 241                 } else {
 242                     checker.add(depth, index);
 243                     try {
 244                         result = child.findNextHandlingNode(varbind,oid,pos+1,
 245                                                             depth+1,handlers,
 246                                                             checker);
 247                     } finally {
 248                         checker.remove(depth);
 249                     }
 250                 }
 251 
 252                 // Build up the leaf OID
 253                 result[depth] = index;
 254                 return result;
 255 
 256             } catch(SnmpStatusException e) {
 257                 // If there is no such element go one level up ...
 258                 //
 259                 index= getNextVarId(index,data,pduVersion);
 260 
 261                 // There is no need to carry the original oid ...
 262                 newOid[0]=index;
 263                 pos= 1;
 264                 oid=newOid;
 265             }
 266         }
 267     }
 268 
 269 
 270     /**
 271      * Computes the root OID of the MIB.
 272      */
 273     @Override
 274     public void getRootOid(Vector<Integer> result) {
 275 
 276         // If a node has several children, let assume that we are one step to
 277         // far in order to get the MIB root.
 278         //
 279         if (nbChildren != 1)
 280             return;
 281 
 282         result.addElement(varList[0]);
 283 
 284         // Now query our child.
 285         //
 286         children.firstElement().getRootOid(result);
 287 
 288     }
 289 
 290     /**
 291      * Registers a specific node in the tree.
 292      */
 293     public void registerNode(String oidString ,SnmpMibNode node)
 294         throws IllegalAccessException {
 295         SnmpOid oid= new SnmpOid(oidString);
 296         registerNode(oid.longValue(), 0, node);
 297     }
 298 
 299     // PROTECTED METHODS
 300     //------------------
 301 
 302     /**
 303      * Registers a specific node in the tree.
 304      */
 305     void registerNode(long[] oid, int cursor ,SnmpMibNode node)
 306         throws IllegalAccessException {
 307 
 308         if (cursor >= oid.length)
 309             throw new IllegalAccessException();
 310 
 311         // Check if the node is already defined
 312         //
 313         long var= oid[cursor];
 314 
 315         //System.out.println("entering registration for val="
 316         // + String.valueOf(var) + " position= " + cursor);
 317 
 318         int pos = retrieveIndex(var);
 319         if (pos  == nbChildren) {
 320             nbChildren++;
 321             varList= new int[nbChildren];
 322             varList[0]= (int) var;
 323             pos =0;
 324             if ( (cursor + 1) == oid.length) {
 325                 // That 's the end of the trip.
 326                 // Do not forward the registration
 327 
 328                 //System.out.println("End of trip for val="
 329                 //      + String.valueOf(var) + " position= " + cursor);
 330                 children.insertElementAt(node,pos);
 331                 return;
 332             }
 333 
 334             //System.out.println("Create node for val="
 335             //       + String.valueOf(var) + " position= " + cursor);
 336             SnmpMibOid child= new SnmpMibOid();
 337             children.insertElementAt(child, pos);
 338             child.registerNode(oid, cursor + 1, node);
 339             return;
 340         }
 341         if (pos == -1) {
 342             // The node is not yet registered
 343             //
 344             int[] tmp= new int[nbChildren + 1];
 345             tmp[nbChildren]= (int) var;
 346             System.arraycopy(varList, 0, tmp, 0, nbChildren);
 347             varList= tmp;
 348             nbChildren++;
 349             SnmpMibNode.sort(varList);
 350             int newPos = retrieveIndex(var);
 351             varList[newPos]= (int) var;
 352             if ( (cursor + 1) == oid.length) {
 353                 // That 's the end of the trip.
 354                 // Do not forward the registration
 355 
 356                 //System.out.println("End of trip for val="
 357                 //     + String.valueOf(var) + " position= " + cursor);
 358                 children.insertElementAt(node, newPos);
 359                 return;
 360             }
 361             SnmpMibOid child= new SnmpMibOid();
 362             // System.out.println("Create node for val=" +
 363             //     String.valueOf(var) + " position= " + cursor);
 364             children.insertElementAt(child, newPos);
 365             child.registerNode(oid, cursor + 1, node);
 366         }
 367         else {
 368             // The node is already registered
 369             //
 370             SnmpMibNode child= children.elementAt(pos);
 371             if ( (cursor + 1) == oid.length ) {
 372                 //System.out.println("Node already registered val=" +
 373                 //          String.valueOf(var) + " position= " + cursor);
 374                 if (child == node) return;
 375                 if (child != null && node != null) {
 376                     // Now we're going to patch the tree the following way:
 377                     //   if a subgroup has been registered before its father,
 378                     //   we're going to replace the father OID node with
 379                     //   the actual group-node and export the children from
 380                     //   the temporary OID node to the actual group node.
 381                     //
 382 
 383                     if (node instanceof SnmpMibGroup) {
 384                         // `node' is a group => replace `child' with `node'
 385                         // export the child's subtree to `node'.
 386                         //
 387                         ((SnmpMibOid)child).exportChildren((SnmpMibOid)node);
 388                         children.setElementAt(node,pos);
 389                         return;
 390 
 391                     } else if ((node instanceof SnmpMibOid) &&
 392                              (child instanceof SnmpMibGroup)) {
 393                         // `node' is a temporary node, and `child' is a
 394                         //  group => keep child and export the node's
 395                         //  subtree to `child'.
 396                         //
 397                         ((SnmpMibOid)node).exportChildren((SnmpMibOid)child);
 398                         return;
 399                     } else if (node instanceof SnmpMibOid) {
 400                         // `node' and `child' are both temporary OID nodes
 401                         // => replace `child' with `node' and export child's
 402                         // subtree to `node'.
 403                         //
 404                         ((SnmpMibOid)child).exportChildren((SnmpMibOid)node);
 405                         children.setElementAt(node,pos);
 406                         return;
 407                     }
 408                 }
 409                 children.setElementAt(node,pos);
 410             } else {
 411                 if (child == null)
 412                     throw new IllegalAccessException();
 413                 ((SnmpMibOid)child).registerNode(oid, cursor + 1, node);
 414             }
 415         }
 416     }
 417 
 418     /**
 419      * Export this node's children to a brother node that will replace
 420      * this node in the OID tree.
 421      * This method is a patch that fixes the problem of registering
 422      * a subnode before its father node.
 423      *
 424      **/
 425     void exportChildren(SnmpMibOid brother)
 426         throws IllegalAccessException {
 427 
 428         if (brother == null) return;
 429         final long[] oid = new long[1];
 430         for (int i=0; i<nbChildren; i++) {
 431             final SnmpMibNode child = children.elementAt(i);
 432             if (child == null) continue;
 433             oid[0] = varList[i];
 434             brother.registerNode(oid,0,child);
 435         }
 436     }
 437 
 438     // PRIVATE METHODS
 439     //----------------
 440 
 441     SnmpMibNode getChild(long id) throws SnmpStatusException {
 442 
 443         // first we need to retrieve the identifier in the list of children
 444         //
 445         final int pos= getInsertAt(id);
 446         if (pos >= nbChildren) {
 447             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 448         }
 449 
 450         if (varList[pos] != (int) id) {
 451             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 452         }
 453 
 454         // Access the node
 455         //
 456         SnmpMibNode child = null;
 457         try {
 458             child = children.elementAtNonSync(pos);
 459         } catch(ArrayIndexOutOfBoundsException e) {
 460             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 461         }
 462         if (child == null) {
 463             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 464         }
 465         return child;
 466     }
 467 
 468     private int retrieveIndex(long val) {
 469 
 470         int low= 0;
 471         int cursor= (int) val;
 472         if (varList == null || varList.length < 1)
 473             return nbChildren;
 474 
 475         int max= varList.length -1 ;
 476         int curr= low + (max-low)/2;
 477         int elmt;
 478         while (low <= max) {
 479             elmt= varList[curr];
 480             if (cursor == elmt) {
 481                 // We need to get the next index ...
 482                 //
 483                 return curr;
 484             }
 485             if (elmt < cursor) {
 486                 low= curr +1;
 487             } else {
 488                 max= curr -1;
 489             }
 490             curr= low + (max-low)/2;
 491         }
 492         return -1;
 493     }
 494 
 495     private int getInsertAt(long val) {
 496 
 497         int low= 0;
 498         final int index= (int) val;
 499         if (varList == null)
 500             return -1;
 501         int max= varList.length -1 ;
 502         int elmt;
 503         //final int[] v = varList;
 504 
 505         //if (index > a[max])
 506         //return max +1;
 507 
 508 
 509         int curr= low + (max-low)/2;
 510         while (low <= max) {
 511 
 512             elmt= varList[curr];
 513 
 514             // never know ...we might find something ...
 515             //
 516             if (index == elmt)
 517                 return curr;
 518 
 519             if (elmt < index) {
 520                 low= curr +1;
 521             } else {
 522                 max= curr -1;
 523             }
 524             curr= low + (max-low)/2;
 525         }
 526 
 527         return curr;
 528     }
 529 
 530     // PRIVATE VARIABLES
 531     //------------------
 532 
 533     /**
 534      * Contains the list of sub nodes.
 535      */
 536     private NonSyncVector<SnmpMibNode> children = new NonSyncVector<>(1);
 537 
 538     /**
 539      * The number of sub nodes.
 540      */
 541     private int nbChildren= 0;
 542 
 543 
 544     // All the methods of the Vector class are synchronized.
 545     // Synchronization is a very expensive operation. In our case it is
 546     // not always required...
 547     //
 548     @SuppressWarnings("serial")  // We will never serialize this
 549     class NonSyncVector<E> extends Vector<E> {
 550 
 551         public NonSyncVector(int size) {
 552             super(size);
 553         }
 554 
 555         final void addNonSyncElement(E obj) {
 556             ensureCapacity(elementCount + 1);
 557             elementData[elementCount++] = obj;
 558         }
 559 
 560         @SuppressWarnings("unchecked")  // cast to E
 561         final E elementAtNonSync(int index) {
 562             return (E) elementData[index];
 563         }
 564 
 565     }
 566 }