1 /*
   2  * Copyright (c) 1999, 2014, 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 package com.sun.jmx.snmp.agent;
  27 
  28 // java imports
  29 //
  30 import java.io.Serializable;
  31 import java.util.Hashtable;
  32 import java.util.Vector;
  33 
  34 // jmx imports
  35 //
  36 import com.sun.jmx.snmp.SnmpVarBind;
  37 import com.sun.jmx.snmp.SnmpStatusException;
  38 
  39 
  40 /**
  41  * Represents a node in an SNMP MIB which corresponds to a group.
  42  * This class allows subnodes to be registered below a group, providing
  43  * support for nested groups. The subnodes are registered at run time
  44  * when registering the nested groups in the global MIB OID tree.
  45  * <P>
  46  * This class is used by the class generated by <CODE>mibgen</CODE>.
  47  * You should not need to use this class directly.
  48  *
  49  * <p><b>This API is a Sun Microsystems internal API  and is subject
  50  * to change without notice.</b></p>
  51  */
  52 @SuppressWarnings("serial") // JDK implementation class
  53 public abstract class SnmpMibGroup extends SnmpMibOid
  54     implements Serializable {
  55 
  56     // We will register the OID arcs leading to subgroups in this hashtable.
  57     // So for each arc in varList, if the arc is also in subgroups, it leads
  58     // to a subgroup, if it is not in subgroup, it leads either to a table
  59     // or to a variable.
  60     protected Hashtable<Long, Long> subgroups = null;
  61 
  62     /**
  63      * Tells whether the given arc identifies a table in this group.
  64      *
  65      * @param arc An OID arc.
  66      *
  67      * @return <CODE>true</CODE> if `arc' leads to a table.
  68      */
  69     public abstract boolean      isTable(long arc);
  70 
  71     /**
  72      * Tells whether the given arc identifies a variable (scalar object) in
  73      * this group.
  74      *
  75      * @param arc An OID arc.
  76      *
  77      * @return <CODE>true</CODE> if `arc' leads to a variable.
  78      */
  79     public abstract boolean      isVariable(long arc);
  80 
  81     /**
  82      * Tells whether the given arc identifies a readable scalar object in
  83      * this group.
  84      *
  85      * @param arc An OID arc.
  86      *
  87      * @return <CODE>true</CODE> if `arc' leads to a readable variable.
  88      */
  89     public abstract boolean      isReadable(long arc);
  90 
  91 
  92     /**
  93      * Gets the table identified by the given `arc'.
  94      *
  95      * @param arc An OID arc.
  96      *
  97      * @return The <CODE>SnmpMibTable</CODE> identified by `arc', or
  98      *    <CODE>null</CODE> if `arc' does not identify any table.
  99      */
 100     public abstract SnmpMibTable getTable(long arc);
 101 
 102     /**
 103      * Checks whether the given OID arc identifies a variable (scalar
 104      * object).
 105      *
 106      * @exception If the given `arc' does not identify any variable in this
 107      *    group, throws an SnmpStatusException.
 108      */
 109     public void validateVarId(long arc, Object userData)
 110         throws SnmpStatusException {
 111         if (isVariable(arc) == false) {
 112             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 113         }
 114     }
 115 
 116 
 117     // -------------------------------------------------------------------
 118     // We use a hashtable (subgroup) in order to determine whether an
 119     // OID arc leads to a subgroup. This implementation can be changed if
 120     // needed...
 121     // For instance, the subclass could provide a generated isNestedArc()
 122     // method in which the subgroup OID arcs would be hardcoded.
 123     // However, the generic approach was preferred because at this time
 124     // groups and subgroups are dynamically registered in the MIB.
 125     //
 126     /**
 127      * Tell whether the given OID arc identifies a sub-tree
 128      * leading to a nested SNMP sub-group. This method is used internally.
 129      * You shouldn't need to call it directly.
 130      *
 131      * @param arc An OID arc.
 132      *
 133      * @return <CODE>true</CODE> if the given OID arc identifies a subtree
 134      * leading to a nested SNMP sub-group.
 135      *
 136      */
 137     public boolean isNestedArc(long arc) {
 138         if (subgroups == null) return false;
 139         Object obj = subgroups.get(arc);
 140         // if the arc is registered in the hashtable,
 141         // it leads to a subgroup.
 142         return (obj != null);
 143     }
 144 
 145     /**
 146      * Generic handling of the <CODE>get</CODE> operation.
 147      * <p>The actual implementation of this method will be generated
 148      * by mibgen. Usually, this implementation only delegates the
 149      * job to some other provided runtime class, which knows how to
 150      * access the MBean. The current toolkit thus provides two
 151      * implementations:
 152      * <ul><li>The standard implementation will directly access the
 153      *         MBean through a java reference,</li>
 154      *     <li>The generic implementation will access the MBean through
 155      *         the MBean server.</li>
 156      * </ul>
 157      * <p>Both implementations rely upon specific - and distinct, set of
 158      * mibgen generated methods.
 159      * <p> You can override this method if you need to implement some
 160      * specific policies for minimizing the accesses made to some remote
 161      * underlying resources.
 162      * <p>
 163      *
 164      * @param req   The sub-request that must be handled by this node.
 165      *
 166      * @param depth The depth reached in the OID tree.
 167      *
 168      * @exception SnmpStatusException An error occurred while accessing
 169      *  the MIB node.
 170      */
 171     @Override
 172     abstract public void get(SnmpMibSubRequest req, int depth)
 173         throws SnmpStatusException;
 174 
 175     /**
 176      * Generic handling of the <CODE>set</CODE> operation.
 177      * <p>The actual implementation of this method will be generated
 178      * by mibgen. Usually, this implementation only delegates the
 179      * job to some other provided runtime class, which knows how to
 180      * access the MBean. The current toolkit thus provides two
 181      * implementations:
 182      * <ul><li>The standard implementation will directly access the
 183      *         MBean through a java reference,</li>
 184      *     <li>The generic implementation will access the MBean through
 185      *         the MBean server.</li>
 186      * </ul>
 187      * <p>Both implementations rely upon specific - and distinct, set of
 188      * mibgen generated methods.
 189      * <p> You can override this method if you need to implement some
 190      * specific policies for minimizing the accesses made to some remote
 191      * underlying resources.
 192      * <p>
 193      *
 194      * @param req   The sub-request that must be handled by this node.
 195      *
 196      * @param depth The depth reached in the OID tree.
 197      *
 198      * @exception SnmpStatusException An error occurred while accessing
 199      *  the MIB node.
 200      */
 201     @Override
 202     abstract public void set(SnmpMibSubRequest req, int depth)
 203         throws SnmpStatusException;
 204 
 205     /**
 206      * Generic handling of the <CODE>check</CODE> operation.
 207      *
 208      * <p>The actual implementation of this method will be generated
 209      * by mibgen. Usually, this implementation only delegates the
 210      * job to some other provided runtime class, which knows how to
 211      * access the MBean. The current toolkit thus provides two
 212      * implementations:
 213      * <ul><li>The standard implementation will directly access the
 214      *         MBean through a java reference,</li>
 215      *     <li>The generic implementation will access the MBean through
 216      *         the MBean server.</li>
 217      * </ul>
 218      * <p>Both implementations rely upon specific - and distinct, set of
 219      * mibgen generated methods.
 220      * <p> You can override this method if you need to implement some
 221      * specific policies for minimizing the accesses made to some remote
 222      * underlying resources, or if you need to implement some consistency
 223      * checks between the different values provided in the varbind list.
 224      * <p>
 225      *
 226      * @param req   The sub-request that must be handled by this node.
 227      *
 228      * @param depth The depth reached in the OID tree.
 229      *
 230      * @exception SnmpStatusException An error occurred while accessing
 231      *  the MIB node.
 232      */
 233     @Override
 234     abstract public void check(SnmpMibSubRequest req, int depth)
 235         throws SnmpStatusException;
 236 
 237     // --------------------------------------------------------------------
 238     // If we reach this node, we are below the root OID, so we just
 239     // return.
 240     // --------------------------------------------------------------------
 241     @Override
 242     public void getRootOid(Vector<Integer> result) {
 243     }
 244 
 245     // -------------------------------------------------------------------
 246     // PACKAGE METHODS
 247     // -------------------------------------------------------------------
 248 
 249     // -------------------------------------------------------------------
 250     // This method can also be overriden in a subclass to provide a
 251     // different implementation of the isNestedArc() method.
 252     // => if isNestedArc() is hardcoded, then registerSubArc() becomes
 253     //    useless and can become empty.
 254     /**
 255      * Register an OID arc that identifies a sub-tree
 256      * leading to a nested SNMP sub-group. This method is used internally.
 257      * You shouldn't ever call it directly.
 258      *
 259      * @param arc An OID arc.
 260      *
 261      */
 262     void registerNestedArc(long arc) {
 263         Long obj = arc;
 264         if (subgroups == null) subgroups = new Hashtable<>();
 265         // registers the arc in the hashtable.
 266         subgroups.put(obj,obj);
 267     }
 268 
 269     // -------------------------------------------------------------------
 270     // The SnmpMibOid algorithm relies on the fact that for every arc
 271     // registered in varList, there is a corresponding node at the same
 272     // position in children.
 273     // So the trick is to register a null node in children for each variable
 274     // in varList, so that the real subgroup nodes can be inserted at the
 275     // correct location.
 276     // registerObject() should be called for each scalar object and each
 277     // table arc by the generated subclass.
 278     /**
 279      * Register an OID arc that identifies a scalar object or a table.
 280      * This method is used internally. You shouldn't ever call it directly.
 281      *
 282      * @param arc An OID arc.
 283      *
 284      */
 285     protected void registerObject(long arc)
 286         throws IllegalAccessException {
 287 
 288         // this will register the variable in both varList and children
 289         // The node registered in children will be null, so that the parent
 290         // algorithm will behave as if no node were registered. This is a
 291         // trick that makes the parent algorithm behave as if only subgroups
 292         // were registered in varList and children.
 293         long[] oid = new long[1];
 294         oid[0] = arc;
 295         super.registerNode(oid,0,null);
 296     }
 297 
 298     // -------------------------------------------------------------------
 299     // registerNode() will be called at runtime when nested groups are
 300     // registered in the MIB. So we do know that this method will only
 301     // be called to register nested-groups.
 302     // We trap registerNode() in order to call registerSubArc()
 303     /**
 304      * Register a child node of this node in the OID tree.
 305      * This method is used internally. You shouldn't ever call it directly.
 306      *
 307      * @param oid The oid of the node being registered.
 308      * @param cursor The position reached in the oid.
 309      * @param node The node being registered.
 310      *
 311      */
 312     @Override
 313     void registerNode(long[] oid, int cursor ,SnmpMibNode node)
 314         throws IllegalAccessException {
 315         super.registerNode(oid,cursor,node);
 316         if (cursor < 0) return;
 317         if (cursor >= oid.length) return;
 318         // if we get here, then it means we are registering a subgroup.
 319         // We will thus register the sub arc in the subgroups hashtable.
 320         registerNestedArc(oid[cursor]);
 321     }
 322 
 323     // -------------------------------------------------------------------
 324     // see comments in SnmpMibNode
 325     // -------------------------------------------------------------------
 326     @Override
 327     void findHandlingNode(SnmpVarBind varbind,
 328                           long[] oid, int depth,
 329                           SnmpRequestTree handlers)
 330         throws SnmpStatusException {
 331 
 332         int length = oid.length;
 333 
 334         if (handlers == null)
 335             throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
 336 
 337         final Object data = handlers.getUserData();
 338 
 339         if (depth >= length) {
 340             // Nothing is left... the oid is not valid
 341             throw new SnmpStatusException(SnmpStatusException.noAccess);
 342         }
 343 
 344         long arc = oid[depth];
 345 
 346         if (isNestedArc(arc)) {
 347             // This arc leads to a subgroup: delegates the search to the
 348             // method defined in SnmpMibOid
 349             super.findHandlingNode(varbind,oid,depth,handlers);
 350         } else if (isTable(arc)) {
 351             // This arc leads to a table: forward the search to the table.
 352 
 353             // Gets the table
 354             SnmpMibTable table = getTable(arc);
 355 
 356             // Forward the search to the table
 357             table.findHandlingNode(varbind,oid,depth+1,handlers);
 358 
 359         } else {
 360             // If it's not a variable, throws an exception
 361             validateVarId(arc, data);
 362 
 363             // The trailing .0 is missing in the OID
 364             if (depth+2 > length) {
 365                 throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 366             }
 367 
 368             // There are too many arcs left in the OID (there should remain
 369             // a single trailing .0)
 370             if (depth+2 < length) {
 371                 throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 372             }
 373 
 374             // The last trailing arc is not .0
 375             if (oid[depth+1] != 0L) {
 376                 throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 377             }
 378 
 379             // It's one of our variable, register this node.
 380             handlers.add(this,depth,varbind);
 381         }
 382     }
 383 
 384     // -------------------------------------------------------------------
 385     // See comments in SnmpMibNode.
 386     // -------------------------------------------------------------------
 387     @Override
 388     long[] findNextHandlingNode(SnmpVarBind varbind,
 389                                 long[] oid, int pos, int depth,
 390                                 SnmpRequestTree handlers, AcmChecker checker)
 391         throws SnmpStatusException {
 392 
 393         int length = oid.length;
 394         SnmpMibNode node = null;
 395 
 396         if (handlers == null) {
 397             // This should be considered as a genErr, but we do not want to
 398             // abort the whole request, so we're going to throw
 399             // a noSuchObject...
 400             //
 401             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 402         }
 403 
 404         final Object data = handlers.getUserData();
 405         final int pduVersion = handlers.getRequestPduVersion();
 406 
 407 
 408         // The generic case where the end of the OID has been reached is
 409         // handled in the superclass
 410         // XXX Revisit: this works but it is somewhat convoluted. Just setting
 411         //              arc to -1 would work too.
 412         if (pos >= length)
 413             return super.findNextHandlingNode(varbind,oid,pos,depth,
 414                                               handlers, checker);
 415 
 416         // Ok, we've got the arc.
 417         long arc = oid[pos];
 418 
 419         long[] result = null;
 420 
 421         // We have a recursive logic. Should we have a loop instead?
 422         try {
 423 
 424             if (isTable(arc)) {
 425                 // If the arc identifies a table, then we need to forward
 426                 // the search to the table.
 427 
 428                 // Gets the table identified by `arc'
 429                 SnmpMibTable table = getTable(arc);
 430 
 431                 // Forward to the table
 432                 checker.add(depth, arc);
 433                 try {
 434                     result = table.findNextHandlingNode(varbind,oid,pos+1,
 435                                                         depth+1,handlers,
 436                                                         checker);
 437                 }catch(SnmpStatusException ex) {
 438                     throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 439                 } finally {
 440                     checker.remove(depth);
 441                 }
 442                 // Build up the leaf OID
 443                 result[depth] = arc;
 444                 return result;
 445             } else if (isReadable(arc)) {
 446                 // If the arc identifies a readable variable, then two cases:
 447 
 448                 if (pos == (length - 1)) {
 449                     // The end of the OID is reached, so we return the leaf
 450                     // corresponding to the variable identified by `arc'
 451 
 452                     // Build up the OID
 453                     // result = new SnmpOid(0);
 454                     // result.insert((int)arc);
 455                     result = new long[depth+2];
 456                     result[depth+1] = 0L;
 457                     result[depth] = arc;
 458 
 459                     checker.add(depth, result, depth, 2);
 460                     try {
 461                         checker.checkCurrentOid();
 462                     } catch(SnmpStatusException e) {
 463                         throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 464                     } finally {
 465                         checker.remove(depth,2);
 466                     }
 467 
 468                     // Registers this node
 469                     handlers.add(this,depth,varbind);
 470                     return result;
 471                 }
 472 
 473                 // The end of the OID is not yet reached, so we must return
 474                 // the next leaf following the variable identified by `arc'.
 475                 // We cannot return the variable because whatever follows in
 476                 // the OID will be greater or equals to 0, and 0 identifies
 477                 // the variable itself - so we have indeed to return the
 478                 // next object.
 479                 // So we do nothing, because this case is handled at the
 480                 // end of the if ... else if ... else ... block.
 481 
 482             } else if (isNestedArc(arc)) {
 483                 // Now if the arc leads to a subgroup, we delegate the
 484                 // search to the child, just as done in SnmpMibNode.
 485                 //
 486 
 487                 // get the child ( = nested arc node).
 488                 //
 489                 final SnmpMibNode child = getChild(arc);
 490 
 491                 if (child != null) {
 492                     checker.add(depth, arc);
 493                     try {
 494                         result = child.findNextHandlingNode(varbind,oid,pos+1,
 495                                                             depth+1,handlers,
 496                                                             checker);
 497                         result[depth] = arc;
 498                         return result;
 499                     } finally {
 500                         checker.remove(depth);
 501                     }
 502                 }
 503             }
 504 
 505             // The oid is not valid, we will throw an exception in order
 506             // to try with the next valid identifier...
 507             //
 508             throw new SnmpStatusException(SnmpStatusException.noSuchObject);
 509 
 510         } catch (SnmpStatusException e) {
 511             // We didn't find anything at the given arc, so we're going
 512             // to try with the next valid arc
 513             //
 514             long[] newOid = new long[1];
 515             newOid[0] = getNextVarId(arc,data,pduVersion);
 516             return findNextHandlingNode(varbind,newOid,0,depth,
 517                                         handlers,checker);
 518         }
 519     }
 520 
 521 }