/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.jmx.snmp.agent;
import java.io.Serializable;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.logging.Level;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
import com.sun.jmx.snmp.EnumRowStatus;
import com.sun.jmx.snmp.SnmpInt;
import com.sun.jmx.snmp.SnmpOid;
import com.sun.jmx.snmp.SnmpStatusException;
import com.sun.jmx.snmp.SnmpValue;
import com.sun.jmx.snmp.SnmpVarBind;
/**
* This class is the base class for SNMP table metadata.
* <p>
* Its responsibility is to manage a sorted array of OID indexes
* according to the SNMP indexing scheme over the "real" table.
* Each object of this class can be bound to an
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} to which it will
* forward remote entry creation requests, and invoke callbacks
* when an entry has been successfully added to / removed from
* the OID index array.
* </p>
*
* <p>
* For each table defined in the MIB, mibgen will generate a specific
* class called Table<i>TableName</i> that will implement the
* SnmpTableEntryFactory interface, and a corresponding
* <i>TableName</i>Meta class that will extend this class. <br>
* The Table<i>TableName</i> class corresponds to the MBean view of the
* table while the <i>TableName</i>Meta class corresponds to the
* MIB metadata view of the same table.
* </p>
*
* <p>
* Objects of this class are instantiated by the generated
* whole MIB class extending {@link com.sun.jmx.snmp.agent.SnmpMib}
* You should never need to instantiate this class directly.
* </p>
*
* <p><b>This API is a Sun Microsystems internal API and is subject
* to change without notice.</b></p>
* @see com.sun.jmx.snmp.agent.SnmpMib
* @see com.sun.jmx.snmp.agent.SnmpMibEntry
* @see com.sun.jmx.snmp.agent.SnmpTableEntryFactory
* @see com.sun.jmx.snmp.agent.SnmpTableSupport
*
*/
public abstract class SnmpMibTable extends SnmpMibNode
implements NotificationBroadcaster, Serializable {
/**
* Create a new <CODE>SnmpMibTable</CODE> metadata node.
*
* <p>
* @param mib The SNMP MIB to which the metadata will be linked.
*/
public SnmpMibTable(SnmpMib mib) {
this.theMib= mib;
setCreationEnabled(false);
}
// -------------------------------------------------------------------
// PUBLIC METHODS
// -------------------------------------------------------------------
/**
* This method is invoked when the creation of a new entry is requested
* by a remote SNMP manager.
* <br>By default, remote entry creation is disabled - and this method
* will not be called. You can dynamically switch the entry creation
* policy by calling <code>setCreationEnabled(true)</code> and <code>
* setCreationEnabled(false)</code> on this object.
* <p><b><i>
* This method is called internally by the SNMP runtime and you
* should never need to call it directly. </b></i>However you might want
* to extend it in order to implement your own specific application
* behaviour, should the default behaviour not be at your convenience.
* </p>
* <p>
* @param req The SNMP subrequest requesting this creation
* @param rowOid The OID indexing the conceptual row (entry) for which
* the creation was requested.
* @param depth The position of the columnar object arc in the OIDs
* from the varbind list.
*
* @exception SnmpStatusException if the entry cannot be created.
*/
public abstract void createNewEntry(SnmpMibSubRequest req, SnmpOid rowOid,
int depth)
throws SnmpStatusException;
/**
* Tell whether the specific version of this metadata generated
* by <code>mibgen</code> requires entries to be registered with
* the MBeanServer. In this case an ObjectName will have to be
* passed to addEntry() in order for the table to behave correctly
* (case of the generic metadata).
* <p>
* If that version of the metadata does not require entry to be
* registered, then passing an ObjectName becomes optional (null
* can be passed instead).
*
* @return <code>true</code> if registration is required by this
* version of the metadata.
*/
public abstract boolean isRegistrationRequired();
/**
* Tell whether a new entry should be created when a SET operation
* is received for an entry that does not exist yet.
*
* @return true if a new entry must be created, false otherwise.<br>
* [default: returns <CODE>false</CODE>]
**/
public boolean isCreationEnabled() {
return creationEnabled;
}
/**
* This method lets you dynamically switch the creation policy.
*
* <p>
* @param remoteCreationFlag Tells whether remote entry creation must
* be enabled or disabled.
* <ul><li>
* <CODE>setCreationEnabled(true)</CODE> will enable remote entry
* creation via SET operations.</li>
* <li>
* <CODE>setCreationEnabled(false)</CODE> will disable remote entry
* creation via SET operations.</li>
* <p> By default remote entry creation via SET operation is disabled.
* </p>
* </ul>
**/
public void setCreationEnabled(boolean remoteCreationFlag) {
creationEnabled = remoteCreationFlag;
}
/**
* Return <code>true</code> if the conceptual row contains a columnar
* object used to control creation/deletion of rows in this table.
* <p>
* This columnar object can be either a variable with RowStatus
* syntax as defined by RFC 2579, or a plain variable whose
* semantics is table specific.
* <p>
* By default, this function returns <code>false</code>, and it is
* assumed that the table has no such control variable.<br>
* When <code>mibgen</code> is used over SMIv2 MIBs, it will generate
* an <code>hasRowStatus()</code> method returning <code>true</code>
* for each table containing an object with RowStatus syntax.
* <p>
* When this method returns <code>false</code> the default mechanism
* for remote entry creation is used.
* Otherwise, creation/deletion is performed as specified
* by the control variable (see getRowAction() for more details).
* <p>
* This method is called internally when a SET request involving
* this table is processed.
* <p>
* If you need to implement a control variable which do not use
* the RowStatus convention as defined by RFC 2579, you should
* subclass the generated table metadata class in order to redefine
* this method and make it returns <code>true</code>.<br>
* You will then have to redefine the isRowStatus(), mapRowStatus(),
* isRowReady(), and setRowStatus() methods to suit your specific
* implementation.
* <p>
* @return <li><code>true</code> if this table contains a control
* variable (eg: a variable with RFC 2579 RowStatus syntax),
* </li>
* <li><code>false</code> if this table does not contain
* any control variable.</li>
*
**/
public boolean hasRowStatus() {
return false;
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode.
//
// ---------------------------------------------------------------------
/**
* Generic handling of the <CODE>get</CODE> operation.
* <p> The default implementation of this method is to
* <ul>
* <li> check whether the entry exists, and if not register an
* exception for each varbind in the list.
* <li> call the generated
* <CODE>get(req,oid,depth+1)</CODE> method. </li>
* </ul>
* <p>
* <pre>
* public void get(SnmpMibSubRequest req, int depth)
* throws SnmpStatusException {
* boolean isnew = req.isNewEntry();
*
* // if the entry does not exists, then registers an error for
* // each varbind involved (nb: this should not happen, since
* // the error should already have been detected earlier)
* //
* if (isnew) {
* SnmpVarBind var = null;
* for (Enumeration e= req.getElements(); e.hasMoreElements();) {
* var = (SnmpVarBind) e.nextElement();
* req.registerGetException(var,noSuchNameException);
* }
* }
*
* final SnmpOid oid = req.getEntryOid();
* get(req,oid,depth+1);
* }
* </pre>
* <p> You should not need to override this method in any cases, because
* it will eventually call
* <CODE>get(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>. If you need to implement
* specific policies for minimizing the accesses made to some remote
* underlying resources, or if you need to implement some consistency
* checks between the different values provided in the varbind list,
* you should then rather override
* <CODE>get(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>.
* <p>
*
*/
@Override
public void get(SnmpMibSubRequest req, int depth)
throws SnmpStatusException {
final boolean isnew = req.isNewEntry();
final SnmpMibSubRequest r = req;
// if the entry does not exists, then registers an error for
// each varbind involved (nb: should not happen, the error
// should have been registered earlier)
if (isnew) {
SnmpVarBind var;
for (Enumeration<SnmpVarBind> e= r.getElements(); e.hasMoreElements();) {
var = e.nextElement();
r.registerGetException(var,new SnmpStatusException(SnmpStatusException.noSuchInstance));
}
}
final SnmpOid oid = r.getEntryOid();
// SnmpIndex index = buildSnmpIndex(oid.longValue(false), 0);
// get(req,index,depth+1);
//
get(req,oid,depth+1);
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode.
//
// ---------------------------------------------------------------------
/**
* Generic handling of the <CODE>check</CODE> operation.
* <p> The default implementation of this method is to
* <ul>
* <li> check whether a new entry must be created, and if remote
* creation of entries is enabled, create it. </li>
* <li> call the generated
* <CODE>check(req,oid,depth+1)</CODE> method. </li>
* </ul>
* <p>
* <pre>
* public void check(SnmpMibSubRequest req, int depth)
* throws SnmpStatusException {
* final SnmpOid oid = req.getEntryOid();
* final int action = getRowAction(req,oid,depth+1);
*
* beginRowAction(req,oid,depth+1,action);
* check(req,oid,depth+1);
* }
* </pre>
* <p> You should not need to override this method in any cases, because
* it will eventually call
* <CODE>check(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>. If you need to implement
* specific policies for minimizing the accesses made to some remote
* underlying resources, or if you need to implement some consistency
* checks between the different values provided in the varbind list,
* you should then rather override
* <CODE>check(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>.
* <p>
*
*/
@Override
public void check(SnmpMibSubRequest req, int depth)
throws SnmpStatusException {
final SnmpOid oid = req.getEntryOid();
final int action = getRowAction(req,oid,depth+1);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"check", "Calling beginRowAction");
}
beginRowAction(req,oid,depth+1,action);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"check",
"Calling check for " + req.getSize() + " varbinds");
}
check(req,oid,depth+1);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"check", "check finished");
}
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode.
//
// ---------------------------------------------------------------------
/**
* Generic handling of the <CODE>set</CODE> operation.
* <p> The default implementation of this method is to
* call the generated
* <CODE>set(req,oid,depth+1)</CODE> method.
* <p>
* <pre>
* public void set(SnmpMibSubRequest req, int depth)
* throws SnmpStatusException {
* final SnmpOid oid = req.getEntryOid();
* final int action = getRowAction(req,oid,depth+1);
*
* set(req,oid,depth+1);
* endRowAction(req,oid,depth+1,action);
* }
* </pre>
* <p> You should not need to override this method in any cases, because
* it will eventually call
* <CODE>set(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>. If you need to implement
* specific policies for minimizing the accesses made to some remote
* underlying resources, or if you need to implement some consistency
* checks between the different values provided in the varbind list,
* you should then rather override
* <CODE>set(SnmpMibSubRequest req, int depth)</CODE> on the generated
* derivative of <CODE>SnmpMibEntry</CODE>.
* <p>
*
*/
@Override
public void set(SnmpMibSubRequest req, int depth)
throws SnmpStatusException {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"set", "Entering set");
}
final SnmpOid oid = req.getEntryOid();
final int action = getRowAction(req,oid,depth+1);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"set", "Calling set for " + req.getSize() + " varbinds");
}
set(req,oid,depth+1);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"set", "Calling endRowAction");
}
endRowAction(req,oid,depth+1,action);
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpMibTable.class.getName(),
"set", "RowAction finished");
}
}
/**
* Add a new entry in this <CODE>SnmpMibTable</CODE>.
* Also triggers the addEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
* This method assumes that the given entry will not be registered.
* If the entry is going to be registered, or if ObjectName's are
* required, then
* {@link com.sun.jmx.snmp.agent.SnmpMibTable#addEntry(SnmpOid,
* ObjectName, Object)} should be preferred.
* <br> This function is mainly provided for backward compatibility.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row to be added.
* @param entry The entry to add.
*
* @exception SnmpStatusException The entry couldn't be added
* at the position identified by the given
* <code>rowOid</code>, or this version of the metadata
* requires ObjectName's.
*/
// public void addEntry(SnmpIndex index, Object entry)
public void addEntry(SnmpOid rowOid, Object entry)
throws SnmpStatusException {
addEntry(rowOid, null, entry);
}
/**
* Add a new entry in this <CODE>SnmpMibTable</CODE>.
* Also triggers the addEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
* <p>
* @param oid The <CODE>SnmpOid</CODE> identifying the table
* row to be added.
*
* @param name The ObjectName with which this entry is registered.
* This parameter can be omitted if isRegistrationRequired()
* return false.
*
* @param entry The entry to add.
*
* @exception SnmpStatusException The entry couldn't be added
* at the position identified by the given
* <code>rowOid</code>, or if this version of the metadata
* requires ObjectName's, and the given name is null.
*/
// protected synchronized void addEntry(SnmpIndex index, ObjectName name,
// Object entry)
public synchronized void addEntry(SnmpOid oid, ObjectName name,
Object entry)
throws SnmpStatusException {
if (isRegistrationRequired() == true && name == null)
throw new SnmpStatusException(SnmpStatusException.badValue);
if (size == 0) {
// indexes.addElement(index);
// XX oids.addElement(oid);
insertOid(0,oid);
if (entries != null)
entries.addElement(entry);
if (entrynames != null)
entrynames.addElement(name);
size++;
// triggers callbacks on the entry factory
//
if (factory != null) {
try {
factory.addEntryCb(0,oid,name,entry,this);
} catch (SnmpStatusException x) {
removeOid(0);
if (entries != null)
entries.removeElementAt(0);
if (entrynames != null)
entrynames.removeElementAt(0);
throw x;
}
}
// sends the notifications
//
sendNotification(SnmpTableEntryNotification.SNMP_ENTRY_ADDED,
(new Date()).getTime(), entry, name);
return;
}
// Get the insertion position ...
//
int pos= 0;
// bug jaw.00356.B : use oid rather than index to get the
// insertion point.
//
pos= getInsertionPoint(oid,true);
if (pos == size) {
// Add a new element in the vectors ...
//
// indexes.addElement(index);
// XX oids.addElement(oid);
insertOid(tablecount,oid);
if (entries != null)
entries.addElement(entry);
if (entrynames != null)
entrynames.addElement(name);
size++;
} else {
// Insert new element ...
//
try {
// indexes.insertElementAt(index, pos);
// XX oids.insertElementAt(oid, pos);
insertOid(pos,oid);
if (entries != null)
entries.insertElementAt(entry, pos);
if (entrynames != null)
entrynames.insertElementAt(name,pos);
size++;
} catch(ArrayIndexOutOfBoundsException e) {
}
}
// triggers callbacks on the entry factory
//
if (factory != null) {
try {
factory.addEntryCb(pos,oid,name,entry,this);
} catch (SnmpStatusException x) {
removeOid(pos);
if (entries != null)
entries.removeElementAt(pos);
if (entrynames != null)
entrynames.removeElementAt(pos);
throw x;
}
}
// sends the notifications
//
sendNotification(SnmpTableEntryNotification.SNMP_ENTRY_ADDED,
(new Date()).getTime(), entry, name);
}
/**
* Remove the specified entry from the table.
* Also triggers the removeEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row to remove.
*
* @param entry The entry to be removed. This parameter is not used
* internally, it is simply passed along to the
* removeEntryCB() callback.
*
* @exception SnmpStatusException if the specified entry couldn't
* be removed (if the given <code>rowOid</code> is not
* valid for instance).
*/
public synchronized void removeEntry(SnmpOid rowOid, Object entry)
throws SnmpStatusException {
int pos = findObject(rowOid);
if (pos == -1)
return;
removeEntry(pos,entry);
}
/**
* Remove the specified entry from the table.
* Also triggers the removeEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row to remove.
*
* @exception SnmpStatusException if the specified entry couldn't
* be removed (if the given <code>rowOid</code> is not
* valid for instance).
*/
public void removeEntry(SnmpOid rowOid)
throws SnmpStatusException {
int pos = findObject(rowOid);
if (pos == -1)
return;
removeEntry(pos,null);
}
/**
* Remove the specified entry from the table.
* Also triggers the removeEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
* <p>
* @param pos The position of the entry in the table.
*
* @param entry The entry to be removed. This parameter is not used
* internally, it is simply passed along to the
* removeEntryCB() callback.
*
* @exception SnmpStatusException if the specified entry couldn't
* be removed.
*/
public synchronized void removeEntry(int pos, Object entry)
throws SnmpStatusException {
if (pos == -1)
return;
if (pos >= size) return;
Object obj = entry;
if (entries != null && entries.size() > pos) {
obj = entries.elementAt(pos);
entries.removeElementAt(pos);
}
ObjectName name = null;
if (entrynames != null && entrynames.size() > pos) {
name = entrynames.elementAt(pos);
entrynames.removeElementAt(pos);
}
final SnmpOid rowOid = tableoids[pos];
removeOid(pos);
size --;
if (obj == null) obj = entry;
if (factory != null)
factory.removeEntryCb(pos,rowOid,name,obj,this);
sendNotification(SnmpTableEntryNotification.SNMP_ENTRY_REMOVED,
(new Date()).getTime(), obj, name);
}
/**
* Get the entry corresponding to the specified rowOid.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the
* row to be retrieved.
*
* @return The entry.
*
* @exception SnmpStatusException There is no entry with the specified
* <code>rowOid</code> in the table.
*/
public synchronized Object getEntry(SnmpOid rowOid)
throws SnmpStatusException {
int pos= findObject(rowOid);
if (pos == -1)
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
return entries.elementAt(pos);
}
/**
* Get the ObjectName of the entry corresponding to the
* specified rowOid.
* The result of this method is only meaningful if
* isRegistrationRequired() yields true.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row whose ObjectName we want to retrieve.
*
* @return The object name of the entry.
*
* @exception SnmpStatusException There is no entry with the specified
* <code>rowOid</code> in the table.
*/
public synchronized ObjectName getEntryName(SnmpOid rowOid)
throws SnmpStatusException {
int pos = findObject(rowOid);
if (entrynames == null) return null;
if (pos == -1 || pos >= entrynames.size())
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
return entrynames.elementAt(pos);
}
/**
* Return the entries stored in this table <CODE>SnmpMibTable</CODE>.
* <p>
* If the subclass generated by mibgen uses the generic way to access
* the entries (i.e. if it goes through the MBeanServer) then some of
* the entries may be <code>null</code>. It all depends whether a non
* <code>null</code> entry was passed to addEntry().<br>
* Otherwise, if it uses the standard way (access the entry directly
* through their standard MBean interface) this array will contain all
* the entries.
* <p>
* @return The entries array.
*/
public Object[] getBasicEntries() {
Object[] array= new Object[size];
entries.copyInto(array);
return array;
}
/**
* Get the size of the table.
*
* @return The number of entries currently registered in this table.
*/
public int getSize() {
return size;
}
// EVENT STUFF
//------------
/**
* Enable to add an SNMP entry listener to this
* <CODE>SnmpMibTable</CODE>.
*
* <p>
* @param listener The listener object which will handle the
* notifications emitted by the registered MBean.
*
* @param filter The filter object. If filter is null, no filtering
* will be performed before handling notifications.
*
* @param handback The context to be sent to the listener when a
* notification is emitted.
*
* @exception IllegalArgumentException Listener parameter is null.
*/
@Override
public synchronized void
addNotificationListener(NotificationListener listener,
NotificationFilter filter, Object handback) {
// Check listener
//
if (listener == null) {
throw new java.lang.IllegalArgumentException
("Listener can't be null") ;
}
// looking for listener in handbackTable
//
Vector<Object> handbackList = handbackTable.get(listener) ;
Vector<NotificationFilter> filterList = filterTable.get(listener) ;
if ( handbackList == null ) {
handbackList = new Vector<>() ;
filterList = new Vector<>() ;
handbackTable.put(listener, handbackList) ;
filterTable.put(listener, filterList) ;
}
// Add the handback and the filter
//
handbackList.addElement(handback) ;
filterList.addElement(filter) ;
}
/**
* Enable to remove an SNMP entry listener from this
* <CODE>SnmpMibTable</CODE>.
*
* @param listener The listener object which will handle the
* notifications emitted by the registered MBean.
* This method will remove all the information related to this
* listener.
*
* @exception ListenerNotFoundException The listener is not registered
* in the MBean.
*/
@Override
public synchronized void
removeNotificationListener(NotificationListener listener)
throws ListenerNotFoundException {
// looking for listener in handbackTable
//
java.util.Vector<?> handbackList = handbackTable.get(listener) ;
if ( handbackList == null ) {
throw new ListenerNotFoundException("listener");
}
// If handback is null, remove the listener entry
//
handbackTable.remove(listener) ;
filterTable.remove(listener) ;
}
/**
* Return a <CODE>NotificationInfo</CODE> object containing the
* notification class and the notification type sent by the
* <CODE>SnmpMibTable</CODE>.
*/
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
String[] types = {SnmpTableEntryNotification.SNMP_ENTRY_ADDED,
SnmpTableEntryNotification.SNMP_ENTRY_REMOVED};
MBeanNotificationInfo[] notifsInfo = {
new MBeanNotificationInfo
(types, "com.sun.jmx.snmp.agent.SnmpTableEntryNotification",
"Notifications sent by the SnmpMibTable")
};
return notifsInfo;
}
/**
* Register the factory through which table entries should
* be created when remote entry creation is enabled.
*
* <p>
* @param factory The
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} through
* which entries will be created when a remote SNMP manager
* request the creation of a new entry via an SNMP SET request.
*/
public void registerEntryFactory(SnmpTableEntryFactory factory) {
this.factory = factory;
}
// ----------------------------------------------------------------------
// PROTECTED METHODS - RowStatus
// ----------------------------------------------------------------------
/**
* Return true if the columnar object identified by <code>var</code>
* is used to control the addition/deletion of rows in this table.
*
* <p>
* By default, this method assumes that there is no control variable
* and always return <code>false</code>
* <p>
* If this table was defined using SMIv2, and if it contains a
* control variable with RowStatus syntax, <code>mibgen</code>
* will generate a non default implementation for this method
* that will identify the RowStatus control variable.
* <p>
* You will have to redefine this method if you need to implement
* control variables that do not conform to RFC 2579 RowStatus
* TEXTUAL-CONVENTION.
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param var The OID arc identifying the involved columnar object.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
**/
protected boolean isRowStatus(SnmpOid rowOid, long var,
Object userData) {
return false;
}
/**
* Return the RowStatus code value specified in this request.
* <p>
* The RowStatus code value should be one of the values defined
* by {@link com.sun.jmx.snmp.EnumRowStatus}. These codes correspond
* to RowStatus codes as defined in RFC 2579, plus the <i>unspecified</i>
* value which is SNMP Runtime specific.
* <p>
*
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @return The RowStatus code specified in this request, if any:
* <ul>
* <li>If the specified row does not exist and this table do
* not use any variable to control creation/deletion of
* rows, then default creation mechanism is assumed and
* <i>createAndGo</i> is returned</li>
* <li>Otherwise, if the row exists and this table do not use any
* variable to control creation/deletion of rows,
* <i>unspecified</i> is returned.</li>
* <li>Otherwise, if the request does not contain the control variable,
* <i>unspecified</i> is returned.</li>
* <li>Otherwise, mapRowStatus() is called to extract the RowStatus
* code from the SnmpVarBind that contains the control variable.</li>
* </ul>
*
* @exception SnmpStatusException if the value of the control variable
* could not be mapped to a RowStatus code.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected int getRowAction(SnmpMibSubRequest req, SnmpOid rowOid,
int depth)
throws SnmpStatusException {
final boolean isnew = req.isNewEntry();
final SnmpVarBind vb = req.getRowStatusVarBind();
if (vb == null) {
if (isnew && ! hasRowStatus())
return EnumRowStatus.createAndGo;
else return EnumRowStatus.unspecified;
}
try {
return mapRowStatus(rowOid, vb, req.getUserData());
} catch( SnmpStatusException x) {
checkRowStatusFail(req, x.getStatus());
}
return EnumRowStatus.unspecified;
}
/**
* Map the value of the <code>vbstatus</code> varbind to the
* corresponding RowStatus code defined in
* {@link com.sun.jmx.snmp.EnumRowStatus}.
* These codes correspond to RowStatus codes as defined in RFC 2579,
* plus the <i>unspecified</i> value which is SNMP Runtime specific.
* <p>
* By default, this method assumes that the control variable is
* an Integer, and it simply returns its value without further
* analysis.
* <p>
* If this table was defined using SMIv2, and if it contains a
* control variable with RowStatus syntax, <code>mibgen</code>
* will generate a non default implementation for this method.
* <p>
* You will have to redefine this method if you need to implement
* control variables that do not conform to RFC 2579 RowStatus
* TEXTUAL-CONVENTION.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param vbstatus The SnmpVarBind containing the value of the control
* variable, as identified by the isRowStatus() method.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The RowStatus code mapped from the value contained
* in <code>vbstatus</code>.
*
* @exception SnmpStatusException if the value of the control variable
* could not be mapped to a RowStatus code.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected int mapRowStatus(SnmpOid rowOid, SnmpVarBind vbstatus,
Object userData)
throws SnmpStatusException {
final SnmpValue rsvalue = vbstatus.value;
if (rsvalue instanceof SnmpInt)
return ((SnmpInt)rsvalue).intValue();
else
throw new SnmpStatusException(
SnmpStatusException.snmpRspInconsistentValue);
}
/**
* Set the control variable to the specified <code>newStatus</code>
* value.
*
* <p>
* This method maps the given <code>newStatus</code> to the appropriate
* value for the control variable, then sets the control variable in
* the entry identified by <code>rowOid</code>. It returns the new
* value of the control variable.
* <p>
* By default, it is assumed that there is no control variable so this
* method does nothing and simply returns <code>null</code>.
* <p>
* If this table was defined using SMIv2, and if it contains a
* control variable with RowStatus syntax, <code>mibgen</code>
* will generate a non default implementation for this method.
* <p>
* You will have to redefine this method if you need to implement
* control variables that do not conform to RFC 2579 RowStatus
* TEXTUAL-CONVENTION.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param newStatus The new status for the row: one of the
* RowStatus code defined in
* {@link com.sun.jmx.snmp.EnumRowStatus}. These codes
* correspond to RowStatus codes as defined in RFC 2579,
* plus the <i>unspecified</i> value which is SNMP Runtime specific.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The new value of the control variable (usually
* <code>new SnmpInt(newStatus)</code>) or <code>null</code>
* if the table do not have any control variable.
*
* @exception SnmpStatusException If the given <code>newStatus</code>
* could not be set on the specified entry, or if the
* given <code>newStatus</code> is not valid.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected SnmpValue setRowStatus(SnmpOid rowOid, int newStatus,
Object userData)
throws SnmpStatusException {
return null;
}
/**
* Tell whether the specified row is ready and can be put in the
* <i>notInService</i> state.
* <p>
* This method is called only once, after all the varbind have been
* set on a new entry for which <i>createAndWait</i> was specified.
* <p>
* If the entry is not yet ready, this method should return false.
* It will then be the responsibility of the entry to switch its
* own state to <i>notInService</i> when it becomes ready.
* No further call to <code>isRowReady()</code> will be made.
* <p>
* By default, this method always return true. <br>
* <code>mibgen</code> will not generate any specific implementation
* for this method - meaning that by default, a row created using
* <i>createAndWait</i> will always be placed in <i>notInService</i>
* state at the end of the request.
* <p>
* If this table was defined using SMIv2, and if it contains a
* control variable with RowStatus syntax, <code>mibgen</code>
* will generate an implementation for this method that will
* delegate the work to the metadata class modelling the conceptual
* row, so that you can override the default behaviour by subclassing
* that metadata class.
* <p>
* You will have to redefine this method if this default mechanism
* does not suit your needs.
*
* <p>
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return <code>true</code> if the row can be placed in
* <i>notInService</i> state.
*
* @exception SnmpStatusException An error occurred while trying
* to retrieve the row status, and the operation should
* be aborted.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected boolean isRowReady(SnmpOid rowOid, Object userData)
throws SnmpStatusException {
return true;
}
/**
* Check whether the control variable of the given row can be
* switched to the new specified <code>newStatus</code>.
* <p>
* This method is called during the <i>check</i> phase of a SET
* request when the control variable specifies <i>active</i> or
* <i>notInService</i>.
* <p>
* By default it is assumed that nothing prevents putting the
* row in the requested state, and this method does nothing.
* It is simply provided as a hook so that specific checks can
* be implemented.
* <p>
* Note that if the actual row deletion fails afterward, the
* atomicity of the request is no longer guaranteed.
*
* <p>
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @param newStatus The new status for the row: one of the
* RowStatus code defined in
* {@link com.sun.jmx.snmp.EnumRowStatus}. These codes
* correspond to RowStatus codes as defined in RFC 2579,
* plus the <i>unspecified</i> value which is SNMP Runtime specific.
*
* @exception SnmpStatusException if switching to this new state
* would fail.
*
**/
protected void checkRowStatusChange(SnmpMibSubRequest req,
SnmpOid rowOid, int depth,
int newStatus)
throws SnmpStatusException {
}
/**
* Check whether the specified row can be removed from the table.
* <p>
* This method is called during the <i>check</i> phase of a SET
* request when the control variable specifies <i>destroy</i>
* <p>
* By default it is assumed that nothing prevents row deletion
* and this method does nothing. It is simply provided as a hook
* so that specific checks can be implemented.
* <p>
* Note that if the actual row deletion fails afterward, the
* atomicity of the request is no longer guaranteed.
*
* <p>
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @exception SnmpStatusException if the row deletion must be
* rejected.
**/
protected void checkRemoveTableRow(SnmpMibSubRequest req, SnmpOid rowOid,
int depth)
throws SnmpStatusException {
}
/**
* Remove a table row upon a remote manager request.
*
* This method is called internally when <code>getRowAction()</code>
* yields <i>destroy</i> - i.e.: it is only called when a remote
* manager requests the removal of a table row.<br>
* You should never need to call this function directly.
* <p>
* By default, this method simply calls <code>removeEntry(rowOid)
* </code>.
* <p>
* You can redefine this method if you need to implement some
* specific behaviour when a remote row deletion is invoked.
* <p>
* Note that specific checks should not be implemented in this
* method, but rather in <code>checkRemoveTableRow()</code>.
* If <code>checkRemoveTableRow()</code> succeeds and this method
* fails afterward, the atomicity of the original SET request can no
* longer be guaranteed.
* <p>
*
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @exception SnmpStatusException if the actual row deletion fails.
* This should not happen since it would break the
* atomicity of the SET request. Specific checks should
* be implemented in <code>checkRemoveTableRow()</code>
* if needed. If the entry does not exists, no exception
* is generated and the method simply returns.
*
**/
protected void removeTableRow(SnmpMibSubRequest req, SnmpOid rowOid,
int depth)
throws SnmpStatusException {
removeEntry(rowOid);
}
/**
* This method takes care of initial RowStatus handling during the
* check() phase of a SET request.
*
* In particular it will:
* <ul><li>check that the given <code>rowAction</code> returned by
* <code>getRowAction()</code> is valid.</li>
* <li>Then depending on the <code>rowAction</code> specified it will:
* <ul><li>either call <code>createNewEntry()</code> (<code>
* rowAction = <i>createAndGo</i> or <i>createAndWait</i>
* </code>),</li>
* <li>or call <code>checkRemoveTableRow()</code> (<code>
* rowAction = <i>destroy</i></code>),</li>
* <li>or call <code>checkRowStatusChange()</code> (<code>
* rowAction = <i>active</i> or <i>notInService</i></code>),</li>
* <li>or generate a SnmpStatusException if the passed <code>
* rowAction</code> is not correct.</li>
* </ul></li></ul>
* <p>
* In principle, you should not need to redefine this method.
* <p>
* <code>beginRowAction()</code> is called during the check phase
* of a SET request, before actual checking on the varbind list
* is performed.
*
* <p>
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @param rowAction The requested action as returned by <code>
* getRowAction()</code>: one of the RowStatus codes defined in
* {@link com.sun.jmx.snmp.EnumRowStatus}. These codes
* correspond to RowStatus codes as defined in RFC 2579,
* plus the <i>unspecified</i> value which is SNMP Runtime specific.
*
* @exception SnmpStatusException if the specified <code>rowAction</code>
* is not valid or cannot be executed.
* This should not happen since it would break the
* atomicity of the SET request. Specific checks should
* be implemented in <code>beginRowAction()</code> if needed.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected synchronized void beginRowAction(SnmpMibSubRequest req,
SnmpOid rowOid, int depth, int rowAction)
throws SnmpStatusException {
final boolean isnew = req.isNewEntry();
final SnmpOid oid = rowOid;
final int action = rowAction;
switch (action) {
case EnumRowStatus.unspecified:
if (isnew) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Failed to create row[" +
rowOid + "] : RowStatus = unspecified");
}
checkRowStatusFail(req,SnmpStatusException.snmpRspNoAccess);
}
break;
case EnumRowStatus.createAndGo:
case EnumRowStatus.createAndWait:
if (isnew) {
if (isCreationEnabled()) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Creating row[" + rowOid +
"] : RowStatus = createAndGo | createAndWait");
}
createNewEntry(req,oid,depth);
} else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Can't create row[" + rowOid +
"] : RowStatus = createAndGo | createAndWait " +
"but creation is disabled");
}
checkRowStatusFail(req,
SnmpStatusException.snmpRspNoAccess);
}
} else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Can't create row[" + rowOid +
"] : RowStatus = createAndGo | createAndWait " +
"but row already exists");
}
checkRowStatusFail(req,
SnmpStatusException.snmpRspInconsistentValue);
}
break;
case EnumRowStatus.destroy:
if (isnew) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction",
"Warning: can't destroy row[" + rowOid +
"] : RowStatus = destroy but row does not exist");
}
} else if (!isCreationEnabled()) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction",
"Can't destroy row[" + rowOid + "] : " +
"RowStatus = destroy but creation is disabled");
}
checkRowStatusFail(req,SnmpStatusException.snmpRspNoAccess);
}
checkRemoveTableRow(req,rowOid,depth);
break;
case EnumRowStatus.active:
case EnumRowStatus.notInService:
if (isnew) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Can't switch state of row[" +
rowOid + "] : specified RowStatus = active | " +
"notInService but row does not exist");
}
checkRowStatusFail(req,
SnmpStatusException.snmpRspInconsistentValue);
}
checkRowStatusChange(req,rowOid,depth,action);
break;
case EnumRowStatus.notReady:
default:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"beginRowAction", "Invalid RowStatus value for row[" +
rowOid + "] : specified RowStatus = " + action);
}
checkRowStatusFail(req,
SnmpStatusException.snmpRspInconsistentValue);
}
}
/**
* This method takes care of final RowStatus handling during the
* set() phase of a SET request.
*
* In particular it will:
* <ul><li>either call <code>setRowStatus(<i>active</i>)</code>
* (<code> rowAction = <i>createAndGo</i> or <i>active</i>
* </code>),</li>
* <li>or call <code>setRowStatus(<i>notInService</i> or <i>
* notReady</i>)</code> depending on the result of <code>
* isRowReady()</code> (<code>rowAction = <i>createAndWait</i>
* </code>),</li>
* <li>or call <code>setRowStatus(<i>notInService</i>)</code>
* (<code> rowAction = <i>notInService</i></code>),
* <li>or call <code>removeTableRow()</code> (<code>
* rowAction = <i>destroy</i></code>),</li>
* <li>or generate a SnmpStatusException if the passed <code>
* rowAction</code> is not correct. This should be avoided
* since it would break SET request atomicity</li>
* </ul>
* <p>
* In principle, you should not need to redefine this method.
* <p>
* <code>endRowAction()</code> is called during the set() phase
* of a SET request, after the actual set() on the varbind list
* has been performed. The varbind containing the control variable
* is updated with the value returned by setRowStatus() (if it is
* not <code>null</code>).
*
* <p>
* @param req The sub-request that must be handled by this node.
*
* @param rowOid The <CODE>SnmpOid</CODE> identifying the table
* row involved in the operation.
*
* @param depth The depth reached in the OID tree.
*
* @param rowAction The requested action as returned by <code>
* getRowAction()</code>: one of the RowStatus codes defined in
* {@link com.sun.jmx.snmp.EnumRowStatus}. These codes
* correspond to RowStatus codes as defined in RFC 2579,
* plus the <i>unspecified</i> value which is SNMP Runtime specific.
*
* @exception SnmpStatusException if the specified <code>rowAction</code>
* is not valid.
*
* @see com.sun.jmx.snmp.EnumRowStatus
**/
protected void endRowAction(SnmpMibSubRequest req, SnmpOid rowOid,
int depth, int rowAction)
throws SnmpStatusException {
final boolean isnew = req.isNewEntry();
final SnmpOid oid = rowOid;
final int action = rowAction;
final Object data = req.getUserData();
SnmpValue value = null;
switch (action) {
case EnumRowStatus.unspecified:
break;
case EnumRowStatus.createAndGo:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction", "Setting RowStatus to 'active' " +
"for row[" + rowOid + "] : requested RowStatus = " +
"createAndGo");
}
value = setRowStatus(oid,EnumRowStatus.active,data);
break;
case EnumRowStatus.createAndWait:
if (isRowReady(oid,data)) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction",
"Setting RowStatus to 'notInService' for row[" +
rowOid + "] : requested RowStatus = createAndWait");
}
value = setRowStatus(oid,EnumRowStatus.notInService,data);
} else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction", "Setting RowStatus to 'notReady' " +
"for row[" + rowOid + "] : requested RowStatus = " +
"createAndWait");
}
value = setRowStatus(oid,EnumRowStatus.notReady,data);
}
break;
case EnumRowStatus.destroy:
if (isnew) {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction",
"Warning: requested RowStatus = destroy, " +
"but row[" + rowOid + "] does not exist");
}
} else {
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction", "Destroying row[" + rowOid +
"] : requested RowStatus = destroy");
}
}
removeTableRow(req,oid,depth);
break;
case EnumRowStatus.active:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction",
"Setting RowStatus to 'active' for row[" +
rowOid + "] : requested RowStatus = active");
}
value = setRowStatus(oid,EnumRowStatus.active,data);
break;
case EnumRowStatus.notInService:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction",
"Setting RowStatus to 'notInService' for row[" +
rowOid + "] : requested RowStatus = notInService");
}
value = setRowStatus(oid,EnumRowStatus.notInService,data);
break;
case EnumRowStatus.notReady:
default:
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST,
SnmpMibTable.class.getName(),
"endRowAction", "Invalid RowStatus value for row[" +
rowOid + "] : specified RowStatus = " + action);
}
setRowStatusFail(req,
SnmpStatusException.snmpRspInconsistentValue);
}
if (value != null) {
final SnmpVarBind vb = req.getRowStatusVarBind();
if (vb != null) vb.value = value;
}
}
// -------------------------------------------------------------------
// PROTECTED METHODS - get next
// -------------------------------------------------------------------
/**
* Return the next OID arc corresponding to a readable columnar
* object in the underlying entry OBJECT-TYPE, possibly skipping over
* those objects that must not or cannot be returned.
* Calls {@link
* #getNextVarEntryId(com.sun.jmx.snmp.SnmpOid,long,java.lang.Object)},
* until
* {@link #skipEntryVariable(com.sun.jmx.snmp.SnmpOid,long,
* java.lang.Object,int)} returns false.
*
*
* @param rowOid The OID index of the row involved in the operation.
*
* @param var Id of the variable we start from, looking for the next.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @param pduVersion Protocol version of the original request PDU.
*
* @return The next columnar object id which can be returned using
* the given PDU's protocol version.
*
* @exception SnmpStatusException If no id is found after the given id.
*
**/
protected long getNextVarEntryId(SnmpOid rowOid,
long var,
Object userData,
int pduVersion)
throws SnmpStatusException {
long varid=var;
do {
varid = getNextVarEntryId(rowOid,varid,userData);
} while (skipEntryVariable(rowOid,varid,userData,pduVersion));
return varid;
}
/**
* Hook for subclasses.
* The default implementation of this method is to always return
* false. Subclasses should redefine this method so that it returns
* true when:
* <ul><li>the variable is a leaf that is not instantiated,</li>
* <li>or the variable is a leaf whose type cannot be returned by that
* version of the protocol (e.g. an Counter64 with SNMPv1).</li>
* </ul>
*
* @param rowOid The OID index of the row involved in the operation.
*
* @param var Id of the variable we start from, looking for the next.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @param pduVersion Protocol version of the original request PDU.
*
* @return true if the variable must be skipped by the get-next
* algorithm.
*/
protected boolean skipEntryVariable(SnmpOid rowOid,
long var,
Object userData,
int pduVersion) {
return false;
}
/**
* Get the <CODE>SnmpOid</CODE> index of the row that follows
* the given <CODE>oid</CODE> in the table. The given <CODE>
* oid</CODE> does not need to be a valid row OID index.
*
* <p>
* @param oid The OID from which the search will begin.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The next <CODE>SnmpOid</CODE> index.
*
* @exception SnmpStatusException There is no index following the
* specified <CODE>oid</CODE> in the table.
*/
protected SnmpOid getNextOid(SnmpOid oid, Object userData)
throws SnmpStatusException {
if (size == 0) {
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
final SnmpOid resOid = oid;
// Just a simple check to speed up retrieval of last element ...
//
// XX SnmpOid last= (SnmpOid) oids.lastElement();
SnmpOid last= tableoids[tablecount-1];
if (last.equals(resOid)) {
// Last element of the table ...
//
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
// First find the oid. This will allow to speed up retrieval process
// during smart discovery of table (using the getNext) as the
// management station will use the valid index returned during a
// previous getNext ...
//
// Returns the position following the position at which resOid
// is found, or the position at which resOid should be inserted.
//
final int newPos = getInsertionPoint(resOid,false);
// If the position returned is not out of bound, we will find
// the next element in the array.
//
if (newPos > -1 && newPos < size) {
try {
// XX last = (SnmpOid) oids.elementAt(newPos);
last = tableoids[newPos];
} catch(ArrayIndexOutOfBoundsException e) {
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
} else {
// We are dealing with the last element of the table ..
//
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
return last;
}
/**
* Return the first entry OID registered in the table.
*
* <p>
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The <CODE>SnmpOid</CODE> of the first entry in the table.
*
* @exception SnmpStatusException If the table is empty.
*/
protected SnmpOid getNextOid(Object userData)
throws SnmpStatusException {
if (size == 0) {
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
// XX return (SnmpOid) oids.firstElement();
return tableoids[0];
}
// -------------------------------------------------------------------
// Abstract Protected Methods
// -------------------------------------------------------------------
/**
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*
* <p> Return the next OID arc corresponding to a readable columnar
* object in the underlying entry OBJECT-TYPE.</p>
*
* <p>
* @param rowOid The OID index of the row involved in the operation.
*
* @param var Id of the variable we start from, looking for the next.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The next columnar object id.
*
* @exception SnmpStatusException If no id is found after the given id.
*
**/
abstract protected long getNextVarEntryId(SnmpOid rowOid, long var,
Object userData)
throws SnmpStatusException;
/**
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*
* <p>
* @param rowOid The OID index of the row involved in the operation.
*
* @param var The var we want to validate.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @exception SnmpStatusException If this id is not valid.
*
*/
abstract protected void validateVarEntryId(SnmpOid rowOid, long var,
Object userData)
throws SnmpStatusException;
/**
*
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*
* <p>
* @param rowOid The OID index of the row involved in the operation.
*
* @param var The OID arc.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @exception SnmpStatusException If this id is not valid.
*
*/
abstract protected boolean isReadableEntryId(SnmpOid rowOid, long var,
Object userData)
throws SnmpStatusException;
/**
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*/
abstract protected void get(SnmpMibSubRequest req,
SnmpOid rowOid, int depth)
throws SnmpStatusException;
/**
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*/
abstract protected void check(SnmpMibSubRequest req,
SnmpOid rowOid, int depth)
throws SnmpStatusException;
/**
* This method is used internally and is implemented by the
* <CODE>SnmpMibTable</CODE> subclasses generated by <CODE>mibgen</CODE>.
*/
abstract protected void set(SnmpMibSubRequest req,
SnmpOid rowOid, int depth)
throws SnmpStatusException;
// ----------------------------------------------------------------------
// PACKAGE METHODS
// ----------------------------------------------------------------------
/**
* Get the <CODE>SnmpOid</CODE> index of the row that follows the
* index extracted from the specified OID array.
* Builds the SnmpOid corresponding to the row OID and calls
* <code>getNextOid(oid,userData)</code>;
*
* <p>
* @param oid The OID array.
*
* @param pos The position in the OID array at which the index starts.
*
* @param userData A contextual object containing user-data.
* This object is allocated through the <code>
* {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
* for each incoming SNMP request.
*
* @return The next <CODE>SnmpOid</CODE>.
*
* @exception SnmpStatusException There is no index following the
* specified one in the table.
*/
SnmpOid getNextOid(long[] oid, int pos, Object userData)
throws SnmpStatusException {
// Construct the sub-oid starting at pos.
// This sub-oid correspond to the oid part just after the entry
// variable oid.
//
final SnmpOid resOid = new SnmpEntryOid(oid,pos);
return getNextOid(resOid,userData);
}
// ---------------------------------------------------------------------
//
// Register an exception when checking the RowStatus variable
//
// ---------------------------------------------------------------------
static void checkRowStatusFail(SnmpMibSubRequest req, int errorStatus)
throws SnmpStatusException {
final SnmpVarBind statusvb = req.getRowStatusVarBind();
final SnmpStatusException x = new SnmpStatusException(errorStatus);
req.registerCheckException(statusvb,x);
}
// ---------------------------------------------------------------------
//
// Register an exception when checking the RowStatus variable
//
// ---------------------------------------------------------------------
static void setRowStatusFail(SnmpMibSubRequest req, int errorStatus)
throws SnmpStatusException {
final SnmpVarBind statusvb = req.getRowStatusVarBind();
final SnmpStatusException x = new SnmpStatusException(errorStatus);
req.registerSetException(statusvb,x);
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode.
//
// ---------------------------------------------------------------------
@Override
final synchronized void findHandlingNode(SnmpVarBind varbind,
long[] oid, int depth,
SnmpRequestTree handlers)
throws SnmpStatusException {
final int length = oid.length;
if (handlers == null)
throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
if (depth >= length)
throw new SnmpStatusException(SnmpStatusException.noAccess);
if (oid[depth] != nodeId)
throw new SnmpStatusException(SnmpStatusException.noAccess);
if (depth+2 >= length)
throw new SnmpStatusException(SnmpStatusException.noAccess);
// Checks that the oid is valid
// validateOid(oid,depth);
// Gets the part of the OID that identifies the entry
final SnmpOid entryoid = new SnmpEntryOid(oid, depth+2);
// Finds the entry: false means that the entry does not exists
final Object data = handlers.getUserData();
final boolean hasEntry = contains(entryoid, data);
// Fails if the entry is not found and the table does not
// not support creation.
// We know that the entry does not exists if (isentry == false).
if (!hasEntry) {
if (!handlers.isCreationAllowed()) {
// we're not doing a set
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
} else if (!isCreationEnabled())
// we're doing a set but creation is disabled.
throw new
SnmpStatusException(SnmpStatusException.snmpRspNoAccess);
}
final long var = oid[depth+1];
// Validate the entry id
if (hasEntry) {
// The entry already exists - validate the id
validateVarEntryId(entryoid,var,data);
}
// Registers this node for the identified entry.
//
if (handlers.isSetRequest() && isRowStatus(entryoid,var,data))
// We only try to identify the RowStatus for SET operations
//
handlers.add(this,depth,entryoid,varbind,(!hasEntry),varbind);
else
handlers.add(this,depth,entryoid,varbind,(!hasEntry));
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode. The algorithm is very
// largely inspired from the original getNext() method.
//
// ---------------------------------------------------------------------
@Override
final synchronized long[] findNextHandlingNode(SnmpVarBind varbind,
long[] oid,
int pos,
int depth,
SnmpRequestTree handlers,
AcmChecker checker)
throws SnmpStatusException {
int length = oid.length;
if (handlers == null) {
// This should be considered as a genErr, but we do not want to
// abort the whole request, so we're going to throw
// a noSuchObject...
//
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
final Object data = handlers.getUserData();
final int pduVersion = handlers.getRequestPduVersion();
long var= -1;
// If the querried oid contains less arcs than the OID of the
// xxxEntry object, we must return the first leaf under the
// first columnar object: the best way to do that is to reset
// the queried oid:
// oid[0] = nodeId (arc of the xxxEntry object)
// pos = 0 (points to the arc of the xxxEntry object)
// then we just have to proceed...
//
if (pos >= length) {
// this will have the side effect to set
// oid[pos] = nodeId
// and
// (pos+1) = length
// so we won't fall into the "else if" cases below -
// so using "else if" rather than "if ..." is guaranteed
// to be safe.
//
oid = new long[1];
oid[0] = nodeId;
pos = 0;
length = 1;
} else if (oid[pos] > nodeId) {
// oid[pos] is expected to be the id of the xxxEntry ...
// The id requested is greater than the id of the xxxEntry,
// so we won't find the next element in this table... (any
// element in this table will have a smaller OID)
//
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
} else if (oid[pos] < nodeId) {
// we must return the first leaf under the first columnar
// object, so we are back to our first case where pos was
// out of bounds... => reset the oid to contain only the
// arc of the xxxEntry object.
//
oid = new long[1];
oid[0] = nodeId;
pos = 0;
length = 0;
} else if ((pos + 1) < length) {
// The arc at the position "pos+1" is the id of the columnar
// object (ie: the id of the variable in the table entry)
//
var = oid[pos+1];
}
// Now that we've got everything right we can begin.
SnmpOid entryoid;
if (pos == (length - 1)) {
// pos points to the last arc in the oid, and this arc is
// guaranteed to be the xxxEntry id (we have handled all
// the other possibilities before)
//
// We must therefore return the first leaf below the first
// columnar object in the table.
//
// Get the first index. If an exception is raised,
// then it means that the table is empty. We thus do not
// have to catch the exception - we let it propagate to
// the caller.
//
entryoid = getNextOid(data);
var = getNextVarEntryId(entryoid,var,data,pduVersion);
} else if ( pos == (length-2)) {
// In that case we have (pos+1) = (length-1), so pos
// points to the arc of the querried variable (columnar object).
// Since the requested oid stops there, it means we have
// to return the first leaf under this columnar object.
//
// So we first get the first index:
// Note: if this raises an exception, this means that the table
// is empty, so we can let the exception propagate to the caller.
//
entryoid = getNextOid(data);
// XXX revisit: not exactly perfect:
// a specific row could be empty.. But we don't know
// how to make the difference! => tradeoff holes
// in tables can't be properly supported (all rows
// must have the same holes)
//
if (skipEntryVariable(entryoid,var,data,pduVersion)) {
var = getNextVarEntryId(entryoid,var,data,pduVersion);
}
} else {
// So now there remain one last case, namely: some part of the
// index is provided by the oid...
// We build a possibly incomplete and invalid index from
// the OID.
// The piece of index provided should begin at pos+2
// oid[pos] = id of the xxxEntry object,
// oid[pos+1] = id of the columnar object,
// oid[pos+2] ... oid[length-1] = piece of index.
//
// We get the next index following the provided index.
// If this raises an exception, then it means that we have
// reached the last index in the table, and we must then
// try with the next columnar object.
//
// Bug fix 4269251
// The SnmpIndex is defined to contain a valid oid:
// this is not an SNMP requirement for the getNext request.
// So we no more use the SnmpIndex but directly the SnmpOid.
//
try {
entryoid = getNextOid(oid, pos + 2, data);
// If the variable must ne skipped, fall through...
//
// XXX revisit: not exactly perfect:
// a specific row could be empty.. But we don't know
// how to make the difference! => tradeoff holes
// in tables can't be properly supported (all rows
// must have the same holes)
//
if (skipEntryVariable(entryoid,var,data,pduVersion)) {
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
} catch(SnmpStatusException se) {
entryoid = getNextOid(data);
var = getNextVarEntryId(entryoid,var,data,pduVersion);
}
}
return findNextAccessibleOid(entryoid,
varbind,
oid,
depth,
handlers,
checker,
data,
var);
}
private long[] findNextAccessibleOid(SnmpOid entryoid,
SnmpVarBind varbind,long[] oid,
int depth, SnmpRequestTree handlers,
AcmChecker checker, Object data,
long var)
throws SnmpStatusException {
final int pduVersion = handlers.getRequestPduVersion();
// Loop on each var (column)
while(true) {
// This should not happen. If it happens, (bug, or customized
// methods returning garbage instead of raising an exception),
// it probably means that there is nothing to return anyway.
// So we throw the exception.
// => will skip to next node in the MIB tree.
//
if (entryoid == null || var == -1 ) {
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
// So here we know both the row (entryoid) and the column (var)
//
try {
// Raising an exception here will make the catch() clause
// switch to the next variable. If `var' is not readable
// for this specific entry, it is not readable for any
// other entry => skip to next column.
//
if (!isReadableEntryId(entryoid,var,data)) {
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
// Prepare the result and the ACM checker.
//
final long[] etable = entryoid.longValue(false);
final int elength = etable.length;
final long[] result = new long[depth + 2 + elength];
result[0] = -1 ; // Bug detector!
// Copy the entryOid at the end of `result'
//
java.lang.System.arraycopy(etable, 0, result,
depth+2, elength);
// Set the node Id and var Id in result.
//
result[depth] = nodeId;
result[depth+1] = var;
// Append nodeId.varId.<rowOid> to ACM checker.
//
checker.add(depth,result,depth,elength+2);
// No we're going to ACM check our OID.
try {
checker.checkCurrentOid();
// No exception thrown by checker => this is all OK!
// we have it: register the handler and return the
// result.
//
handlers.add(this,depth,entryoid,varbind,false);
return result;
} catch(SnmpStatusException e) {
// Skip to the next entry. If an exception is
// thrown, will be catch by enclosing catch
// and a skip is done to the next var.
//
entryoid = getNextOid(entryoid, data);
} finally {
// Clean the checker.
//
checker.remove(depth,elength+2);
}
} catch(SnmpStatusException e) {
// Catching an exception here means we have to skip to the
// next column.
//
// Back to the first row.
entryoid = getNextOid(data);
// Find out the next column.
//
var = getNextVarEntryId(entryoid,var,data,pduVersion);
}
// This should not happen. If it happens, (bug, or customized
// methods returning garbage instead of raising an exception),
// it probably means that there is nothing to return anyway.
// No need to continue, we throw an exception.
// => will skip to next node in the MIB tree.
//
if (entryoid == null || var == -1 ) {
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
}
}
/**
* Validate the specified OID.
*
* <p>
* @param oid The OID array.
*
* @param pos The position in the array.
*
* @exception SnmpStatusException If the validation fails.
*/
final void validateOid(long[] oid, int pos) throws SnmpStatusException {
final int length= oid.length;
// Control the length of the oid
//
if (pos +2 >= length) {
throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
}
// Check that the entry identifier is specified
//
if (oid[pos] != nodeId) {
throw new SnmpStatusException(SnmpStatusException.noSuchObject);
}
}
// ----------------------------------------------------------------------
// PRIVATE METHODS
// ----------------------------------------------------------------------
/**
* Enable this <CODE>SnmpMibTable</CODE> to send a notification.
*
* <p>
* @param notification The notification to send.
*/
private synchronized void sendNotification(Notification notification) {
// loop on listener
//
for(java.util.Enumeration<NotificationListener> k = handbackTable.keys();
k.hasMoreElements(); ) {
NotificationListener listener = k.nextElement();
// Get the associated handback list and the associated filter list
//
java.util.Vector<?> handbackList = handbackTable.get(listener) ;
java.util.Vector<NotificationFilter> filterList =
filterTable.get(listener) ;
// loop on handback
//
java.util.Enumeration<NotificationFilter> f = filterList.elements();
/**代码未完, 请加载全部代码(NowJava.com).**/