All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.snmp4j.util.TableUtils Maven / Gradle / Ivy

/*_############################################################################
  _## 
  _##  SNMP4J 2 - TableUtils.java  
  _## 
  _##  Copyright (C) 2003-2016  Frank Fock and Jochen Katz (SNMP4J.org)
  _##  
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _##  
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _##  
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _##  
  _##########################################################################*/
package org.snmp4j.util;

import java.util.*;

import org.snmp4j.log.*;
import org.snmp4j.*;
import org.snmp4j.event.*;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.*;
import java.io.*;

/**
 * The TableUtils class provides utility functions to retrieve
 * SNMP tabular data.
 *
 * @author Frank Fock
 * @version 1.6e
 * @since 1.0.2
 */
public class TableUtils extends AbstractSnmpUtility {

  private static final LogAdapter logger =
      LogFactory.getLogger(TableUtils.class);

  // RowStatus TC enumerated values
  public static final int ROWSTATUS_ACTIVE = 1;
  public static final int ROWSTATUS_NOTINSERVICE = 2;
  public static final int ROWSTATUS_NOTREADY = 3;
  public static final int ROWSTATUS_CREATEANDGO = 4;
  public static final int ROWSTATUS_CREATEANDWAIT = 5;
  public static final int ROWSTATUS_DESTROY = 6;

  private int maxNumOfRowsPerPDU = 10;
  private int maxNumColumnsPerPDU = 10;

  /**
   * Creates a TableUtils instance. The created instance is thread
   * safe as long as the supplied Session and PDUFactory
   * are thread safe.
   *
   * @param snmpSession
   *    a SNMP Session instance.
   * @param pduFactory
   *    a PDUFactory instance that creates the PDU that are used
   *    by this instance to retrieve table data using GETBULK/GETNEXT
   *    operations.
   */
  public TableUtils(Session snmpSession, PDUFactory pduFactory) {
    super(snmpSession, pduFactory);
  }

  /**
   * Gets synchronously SNMP tabular data from one or more tables.
   * The data is returned row-by-row as a list of {@link TableEvent} instances.
   * Each instance represents a row (or an error condition). Besides the
   * target agent, the OIDs of the columnar objects have to be specified
   * for which instances should be retrieved. With a lower bound index and
   * an upper bound index, the result set can be narrowed to improve
   * performance. This method can be executed concurrently by multiple threads.
   *
   * @param target
   *    a Target instance.
   * @param columnOIDs
   *    an array of OIDs of the columnar objects whose instances should be
   *    retrieved. The columnar objects may belong to different tables.
   *    Typically they belong to tables that share a common index or sub-index
   *    prefix. Note: The result of this method is not defined if instance OIDs
   *    are supplied in this array!
   * @param lowerBoundIndex
   *    an optional parameter that specifies the lower bound index.
   *    If not null, all returned rows have an index greater than
   *    lowerBoundIndex.
   * @param upperBoundIndex
   *    an optional parameter that specifies the upper bound index.
   *    If not null, all returned rows have an index less or equal
   *    than upperBoundIndex.
   * @return
   *    a List of {@link TableEvent} instances. Each instance
   *    represents successfully retrieved row or an error condition. Error
   *    conditions (any status other than {@link TableEvent#STATUS_OK})
   *    may only appear at the last element of the list.
   */
  public List getTable(Target target,
                       OID[] columnOIDs,
                       OID lowerBoundIndex,
                       OID upperBoundIndex) {

    if ((columnOIDs == null) || (columnOIDs.length == 0)) {
      throw new IllegalArgumentException("No column OIDs specified");
    }
    InternalTableListener listener = new InternalTableListener();
    TableRequest req = createTableRequest(target, columnOIDs, listener,
                                          null,
                                          lowerBoundIndex,
                                          upperBoundIndex);
    synchronized (listener) {
      if (req.sendNextChunk()) {
        try {
          while (!listener.isFinished()) {
            listener.wait();
          }
        }
        catch (InterruptedException ex) {
          Thread.currentThread().interrupt();
        }
      }
    }
    return listener.getRows();
  }

  protected TableRequest createTableRequest(Target target, OID[] columnOIDs,
                                            TableListener listener,
                                            Object userObject,
                                            OID lowerBoundIndex,
                                            OID upperBoundIndex) {
    return new TableRequest(target, columnOIDs, listener,
                            userObject, lowerBoundIndex, upperBoundIndex);
  }

  /**
   * Gets SNMP tabular data from one or more tables. The data is returned
   * asynchronously row-by-row through a supplied callback. Besides the
   * target agent, the OIDs of the columnar objects have to be specified
   * for which instances should be retrieved. With a lower bound index and
   * an upper bound index, the result set can be narrowed to improve
   * performance.
   * 

* This method may call the {@link TableListener#finished} method before * it returns. If you want to synchronize the main thread with the * finishing of the table retrieval, follow this pattern: *

   *      synchronized (this) {
   *         TableListener myListener = ... {
   *            private boolean finished;
   *
   *            public boolean isFinished() {
   *              return finished;
   *            }
   *
   *            public void finished(TableEvent event) {
   *               ..
   *               finished = true;
   *               synchronized (event.getUserObject()) {
   *                  event.getUserObject().notify();
   *               }
   *            }
   *         };
   *         tableUtil.getTable(..,..,myListener,this,..,..);
   *         while (!myListener.isFinished()) {
   *           wait();
   *         }
   *      }
   * 
* * @param target * a Target instance. * @param columnOIDs * an array of OIDs of the columnar objects whose instances should be * retrieved. The columnar objects may belong to different tables. * Typically they belong to tables that share a common index or sub-index * prefix. Note: The result of this method is not defined if instance OIDs * are supplied in this array! * @param listener * a TableListener that is called with {@link TableEvent} * objects when an error occured, new rows have been retrieved, or when * the table has been retrieved completely. * @param userObject * an user object that is transparently supplied to the above call back. * @param lowerBoundIndex * an optional parameter that specifies the lower bound index. * If not null, all returned rows have an index greater than * lowerBoundIndex. * @param upperBoundIndex * an optional parameter that specifies the upper bound index. * If not null, all returned rows have an index less or equal * than upperBoundIndex. */ public void getTable(Target target, OID[] columnOIDs, TableListener listener, Object userObject, OID lowerBoundIndex, OID upperBoundIndex) { if ((columnOIDs == null) || (columnOIDs.length == 0)) { throw new IllegalArgumentException("No column OIDs specified"); } TableRequest req = new TableRequest(target, columnOIDs, listener, userObject, lowerBoundIndex, upperBoundIndex); req.sendNextChunk(); } /** * Gets SNMP tabular data from one or more tables. The data is returned * asynchronously row-by-row through a supplied callback. Besides the * target agent, the OIDs of the columnar objects have to be specified * for which instances should be retrieved. With a lower bound index and * an upper bound index, the result set can be narrowed to improve * performance. *

* This implementation must not be used with sparese tables, because it * is optimized for dense tables and will not return correct results for * sparse tables. *

* * @param target * a Target instance. * @param columnOIDs * an array of OIDs of the columnar objects whose instances should be * retrieved. The columnar objects may belong to different tables. * Typically they belong to tables that share a common index or sub-index * prefix. Note: The result of this method is not defined if instance OIDs * are supplied in this array! * @param listener * a TableListener that is called with {@link TableEvent} * objects when an error occurred, new rows have been retrieved, or when * the table has been retrieved completely. * @param userObject * an user object that is transparently supplied to the above call back. * @param lowerBoundIndex * an optional parameter that specifies the lower bound index. * If not null, all returned rows have an index greater than * lowerBoundIndex. * @param upperBoundIndex * an optional parameter that specifies the upper bound index. * If not null, all returned rows have an index less or equal * than lowerBoundIndex. * @since 1.5 */ public void getDenseTable(Target target, OID[] columnOIDs, TableListener listener, Object userObject, OID lowerBoundIndex, OID upperBoundIndex) { if ((columnOIDs == null) || (columnOIDs.length == 0)) { throw new IllegalArgumentException("No column OIDs specified"); } TableRequest req = new TableRequest(target, columnOIDs, listener, userObject, lowerBoundIndex, upperBoundIndex); req.sendNextChunk(); } /** * Gets the maximum number of rows that will be retrieved per SNMP GETBULK * request. * * @return * an integer greater than zero that specifies the maximum number of rows * to retrieve per SNMP GETBULK operation. */ public int getMaxNumRowsPerPDU() { return maxNumOfRowsPerPDU; } /** * Sets the maximum number of rows that will be retrieved per SNMP GETBULK * request. The default is 10. * * @param numberOfRowsPerChunk * an integer greater than zero that specifies the maximum number of rows * to retrieve per SNMP GETBULK operation. */ public void setMaxNumRowsPerPDU(int numberOfRowsPerChunk) { if (numberOfRowsPerChunk < 1) { throw new IllegalArgumentException( "The number of rows per PDU must be > 0"); } this.maxNumOfRowsPerPDU = numberOfRowsPerChunk; } /** * Gets the maximum number of columns that will be retrieved per SNMP GETNEXT * or GETBULK request. * * @return * an integer greater than zero that specifies the maximum columns of rows * to retrieve per SNMP GETNEXT or GETBULK operation. */ public int getMaxNumColumnsPerPDU() { return maxNumColumnsPerPDU; } /** * Sets the maximum number of columns that will be retrieved per SNMP GETNEXT * or GETBULK request. The default is 10. * * @param numberOfColumnsPerChunk * an integer greater than zero that specifies the maximum columns of rows * to retrieve per SNMP GETNEXT or GETBULK operation. */ public void setMaxNumColumnsPerPDU(int numberOfColumnsPerChunk) { if (numberOfColumnsPerChunk < 1) { throw new IllegalArgumentException( "The number of columns per PDU must be > 0"); } this.maxNumColumnsPerPDU = numberOfColumnsPerChunk; } public class TableRequest implements ResponseListener { Target target; OID[] columnOIDs; TableListener listener; Object userObject; OID lowerBoundIndex; OID upperBoundIndex; private int sent = 0; private boolean anyMatch = false; private Vector lastSent = null; private LinkedList rowCache = new LinkedList(); protected Vector lastReceived; volatile boolean finished = false; public TableRequest(Target target, OID[] columnOIDs, TableListener listener, Object userObject, OID lowerBoundIndex, OID upperBoundIndex) { this.target = target; this.columnOIDs = columnOIDs; this.listener = listener; this.userObject = userObject; this.lastReceived = new Vector(Arrays.asList(columnOIDs)); this.upperBoundIndex = upperBoundIndex; this.lowerBoundIndex = lowerBoundIndex; if (lowerBoundIndex != null) { for (int i = 0; i < lastReceived.size(); i++) { OID oid = new OID((lastReceived.get(i))); oid.append(lowerBoundIndex); lastReceived.set(i, oid); } } } public boolean sendNextChunk() { if (sent >= lastReceived.size()) { return false; } PDU pdu = pduFactory.createPDU(target); if (target.getVersion() == SnmpConstants.version1) { pdu.setType(PDU.GETNEXT); } else if (pdu.getType() != PDU.GETNEXT) { pdu.setType(PDU.GETBULK); } int sz = Math.min(lastReceived.size() - sent, maxNumColumnsPerPDU); if (pdu.getType() == PDU.GETBULK) { if (maxNumOfRowsPerPDU > 0) { pdu.setMaxRepetitions(maxNumOfRowsPerPDU); pdu.setNonRepeaters(0); } else { pdu.setNonRepeaters(sz); pdu.setMaxRepetitions(0); } } lastSent = new Vector(sz + 1); List sentColumns = new ArrayList(sz); int chunkSize = 0; for (int i = sent; i < sent + sz; i++) { OID col = lastReceived.get(i); // only sent columns that are not complete yet if (col.startsWith(columnOIDs[i])) { VariableBinding vb = new VariableBinding(col); pdu.add(vb); if (pdu.getBERLength() > target.getMaxSizeRequestPDU()) { pdu.trim(); break; } else { lastSent.add(lastReceived.get(i)); chunkSize++; } sentColumns.add(i); } else { chunkSize++; } } try { sent += chunkSize; if (pdu.size() == 0) { return false; } sendRequest(pdu, target, sentColumns); } catch (Exception ex) { logger.error(ex); if (logger.isDebugEnabled()) { ex.printStackTrace(); } listener.finished(new TableEvent(this, userObject, ex)); return false; } return true; } protected void sendRequest(PDU pdu, Target target, List sendColumns) throws IOException { session.send(pdu, target, sendColumns, this); } @SuppressWarnings("unchecked") public void onResponse(ResponseEvent event) { // Do not forget to cancel the asynchronous request! ;-) session.cancel(event.getRequest(), this); if (finished) { return; } synchronized (this) { if (checkResponse(event)) { boolean anyMatchInChunk = false; List colsOfRequest = (List) event.getUserObject(); PDU request = event.getRequest(); PDU response = event.getResponse(); int cols = request.size(); int rows = response.size() / cols; OID lastMinIndex = null; for (int r = 0; r < rows; r++) { Row row = null; anyMatchInChunk = false; for (int c = 0; c < request.size(); c++) { int pos = colsOfRequest.get(c); VariableBinding vb = response.get(r * cols + c); if (vb.isException()) { continue; } OID id = vb.getOid(); OID col = columnOIDs[pos]; if (id.startsWith(col)) { OID index = new OID(id.getValue(), col.size(), id.size() - col.size()); if ((upperBoundIndex != null) && (index.compareTo(upperBoundIndex) > 0)) { continue; } if ((lastMinIndex == null) || (index.compareTo(lastMinIndex) < 0)) { lastMinIndex = index; } anyMatchInChunk = true; if ((row == null) || (!row.getRowIndex().equals(index))) { row = null; for (ListIterator it = rowCache.listIterator(rowCache.size()); it.hasPrevious(); ) { Row lastRow = it.previous(); int compareResult = index.compareTo(lastRow.getRowIndex()); if (compareResult == 0) { row = lastRow; break; } else if (compareResult > 0) { break; } } } if (row == null) { row = new Row(index); if (rowCache.size() == 0) { rowCache.add(row); } else if ((rowCache.getFirst()).getRowIndex().compareTo( index) >= 0) { rowCache.addFirst(row); } else { for (ListIterator it = rowCache.listIterator(rowCache.size()); it.hasPrevious(); ) { Row lastRow = it.previous(); if (index.compareTo(lastRow.index) >= 0) { it.set(row); it.add(lastRow); break; } } } } if (((!row.setNumComplete(pos)) || (row.size() > pos)) && (row.get(pos) != null)) { finished = true; listener.finished(new TableEvent(this, userObject, TableEvent.STATUS_WRONG_ORDER)); return; } row.setNumComplete(pos); if (pos < row.getNumComplete()) { row.set(pos, vb); } else { row.add(vb); } lastReceived.set(pos, vb.getOid()); } else { lastReceived.set(pos, vb.getOid()); } } } anyMatch |= anyMatchInChunk; while ((rowCache.size() > 0) && ((rowCache.getFirst()).getNumComplete() == columnOIDs.length) && ((lastMinIndex == null) || ((rowCache.getFirst()).getRowIndex().compareTo( lastMinIndex) < 0))) { if (!listener.next(getTableEvent())) { finished = true; listener.finished(new TableEvent(this, userObject)); return; } } if (!sendNextChunk()) { if (anyMatch) { sent = 0; anyMatch = false; sendNextChunk(); } else { emptyCache(); finished = true; listener.finished(new TableEvent(this, userObject)); } } } } } protected boolean checkResponse(ResponseEvent event) { if (event.getError() != null) { finished = true; emptyCache(); listener.finished(new TableEvent(this, userObject, event.getError())); } else if (event.getResponse() == null) { finished = true; // timeout emptyCache(); listener.finished(new TableEvent(this, userObject, TableEvent.STATUS_TIMEOUT)); } else if (event.getResponse().getType() == PDU.REPORT) { finished = true; emptyCache(); listener.finished(new TableEvent(this, userObject, event.getResponse())); } else if (event.getResponse().getErrorStatus() != PDU.noError) { finished = true; emptyCache(); listener.finished(new TableEvent(this, userObject, event.getResponse().getErrorStatus())); } else { return true; } return false; } private void emptyCache() { while (rowCache.size() > 0) { if (!listener.next(getTableEvent())) { break; } } } private TableEvent getTableEvent() { Row r = rowCache.removeFirst(); r.setNumComplete(columnOIDs.length); VariableBinding[] vbs = new VariableBinding[r.size()]; r.copyInto(vbs); return new TableEvent(this, userObject, r.getRowIndex(), vbs); } public Row getRow(OID index) { for (ListIterator it = rowCache.listIterator(rowCache.size() + 1); it.hasPrevious(); ) { Row r = it.previous(); if (index.equals(r.getRowIndex())) { return r; } } return null; } } /** * The DenseTableRequest extends TableRequest to implement a * faster table retrieval than the original. Caution: * This version does not correctly retrieve sparse tables! * * @author Frank Fock * @since 1.5 */ class DenseTableRequest extends TableRequest { protected DenseTableRequest(Target target, OID[] columnOIDs, TableListener listener, Object userObject, OID lowerBoundIndex, OID upperBoundIndex) { super(target, columnOIDs, listener, userObject, lowerBoundIndex, upperBoundIndex); } public synchronized void onResponse(ResponseEvent event) { // Do not forget to cancel the asynchronous request! ;-) session.cancel(event.getRequest(), this); if (finished) { return; } if (checkResponse(event)) { int startCol = (Integer) event.getUserObject(); PDU request = event.getRequest(); PDU response = event.getResponse(); int cols = request.size(); int rows = response.size() / cols; OID lastMinIndex = null; for (int r = 0; r < rows; r++) { Row row = null; for (int c = 0; c < request.size(); c++) { int pos = startCol + c; VariableBinding vb = response.get(r * cols + c); if (vb.isException()) { continue; } OID id = vb.getOid(); OID col = columnOIDs[pos]; if (id.startsWith(col)) { OID index = new OID(id.getValue(), col.size(), id.size() - col.size()); if ((upperBoundIndex != null) && (index.compareTo(upperBoundIndex) > 0)) { continue; } if ((lastMinIndex == null) || (index.compareTo(lastMinIndex) < 0)) { lastMinIndex = index; } if (row == null) { row = new Row(index); } row.setNumComplete(pos); if (pos < row.getNumComplete()) { row.set(pos, vb); } else { row.add(vb); } lastReceived.set(pos, vb.getOid()); } } if (row != null) { if (!listener.next(new TableEvent(this, userObject, row.getRowIndex(), row.toArray(new VariableBinding[row.size()])))) { finished = true; listener.finished(new TableEvent(this, userObject)); return; } } } if (!sendNextChunk()) { finished = true; listener.finished(new TableEvent(this, userObject)); } } } } /** * Creates a SNMP table row for a table that supports the RowStatus * mechanism for row creation. * @param target * the Target SNMP entity for the operation. * @param rowStatusColumnOID * the column OID of the RowStatus column (without any instance identifier). * @param rowIndex * the OID denoting the index of the table row to create. * @param values * the values of columns to set in the row. If values is * null the row is created via the tripple mode row creation * mechanism (RowStatus is set to createAndWait). Otherwise, each variable * binding has to contain the OID of the columnar object ID (without any * instance identifier) and its value. On return, the variable bindings * will be modified so that the variable binding OIDs will contain the * instance OIDs of the respective columns (thus column OID + rowIndex). * @return ResponseEvent * the ResponseEvent instance returned by the SNMP session on response * of the internally sent SET request. If null, an IO * exception has occurred. Otherwise, if the response PDU is * null a timeout has occurred. Otherwise, check the error * status for {@link SnmpConstants#SNMP_ERROR_SUCCESS} to verify that the * row creation was successful. * @since 1.6 */ public ResponseEvent createRow(Target target, OID rowStatusColumnOID, OID rowIndex, VariableBinding[] values) { PDU pdu = pduFactory.createPDU(target); OID rowStatusID = new OID(rowStatusColumnOID); rowStatusID.append(rowIndex); VariableBinding rowStatus = new VariableBinding(rowStatusID); if (values != null) { // one-shot mode rowStatus.setVariable(new Integer32(ROWSTATUS_CREATEANDGO)); } else { rowStatus.setVariable(new Integer32(ROWSTATUS_CREATEANDWAIT)); } pdu.add(rowStatus); if (values != null) { // append index to all columnar values for (VariableBinding value : values) { OID columnOID = new OID(value.getOid()); columnOID.append(rowIndex); value.setOid(columnOID); } pdu.addAll(values); } pdu.setType(PDU.SET); try { return session.send(pdu, target); } catch (IOException ex) { logger.error(ex); } return null; } /** * Destroys a SNMP table row from a table that support the RowStatus * mechanism for row creation/deletion. * @param target * the Target SNMP entity for the operation. * @param rowStatusColumnOID * the column OID of the RowStatus column (without any instance identifier). * @param rowIndex * the OID denoting the index of the table row to destroy. * @return ResponseEvent * the ResponseEvent instance returned by the SNMP session on response * of the internally sent SET request. If null, an IO * exception has occurred. Otherwise, if the response PDU is * null a timeout has occured, Otherwise, check the error * status for {@link SnmpConstants#SNMP_ERROR_SUCCESS} to verify that the * row creation was successful. * @since 1.7.6 */ public ResponseEvent destroyRow(Target target, OID rowStatusColumnOID, OID rowIndex) { PDU pdu = pduFactory.createPDU(target); OID rowStatusID = new OID(rowStatusColumnOID); rowStatusID.append(rowIndex); VariableBinding rowStatus = new VariableBinding(rowStatusID); rowStatus.setVariable(new Integer32(ROWSTATUS_DESTROY)); pdu.add(rowStatus); pdu.setType(PDU.SET); try { ResponseEvent responseEvent = session.send(pdu, target); return responseEvent; } catch (IOException ex) { logger.error(ex); } return null; } class Row extends Vector { private static final long serialVersionUID = -2297277440117636627L; private OID index; public Row(OID index) { super(); this.index = index; } public OID getRowIndex() { return index; } public int getNumComplete() { return super.size(); } public boolean setNumComplete(int numberOfColumnsComplete) { int sz = numberOfColumnsComplete - getNumComplete(); for (int i = 0; i < sz; i++) { super.add(null); } return (sz >= 0); } } class InternalTableListener implements TableListener { private List rows = new LinkedList(); private volatile boolean finished = false; public boolean next(TableEvent event) { rows.add(event); return true; } public synchronized void finished(TableEvent event) { if ((event.getStatus() != TableEvent.STATUS_OK) || (event.getIndex() != null)) { rows.add(event); } finished = true; notify(); } public List getRows() { return rows; } public boolean isFinished() { return finished; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy