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 }