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(new Long(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 = new Long(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 }