/*
* Copyright (c) 1997, 2007, 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.
*
* 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.
*
*
*
* For each table defined in the MIB, mibgen will generate a specific
* class called TableTableName that will implement the
* SnmpTableEntryFactory interface, and a corresponding
* TableNameMeta class that will extend this class.
* The TableTableName class corresponds to the MBean view of the
* table while the TableNameMeta class corresponds to the
* MIB metadata view of the same table.
*
*
*
* 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.
*
*
*
This API is a Sun Microsystems internal API and is subject
* to change without notice.
* @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 SnmpMibTable metadata node.
*
*
* @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.
* By default, remote entry creation is disabled - and this method
* will not be called. You can dynamically switch the entry creation
* policy by calling setCreationEnabled(true) and
* setCreationEnabled(false) on this object.
*
* This method is called internally by the SNMP runtime and you
* should never need to call it directly. 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.
*
*
* @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 mibgen 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).
*
* 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 true 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.
* [default: returns false]
**/
public boolean isCreationEnabled() {
return creationEnabled;
}
/**
* This method lets you dynamically switch the creation policy.
*
*
* @param remoteCreationFlag Tells whether remote entry creation must
* be enabled or disabled.
*
* setCreationEnabled(true) will enable remote entry
* creation via SET operations.
*
* setCreationEnabled(false) will disable remote entry
* creation via SET operations.
*
By default remote entry creation via SET operation is disabled.
*
*
**/
public void setCreationEnabled(boolean remoteCreationFlag) {
creationEnabled = remoteCreationFlag;
}
/**
* Return true if the conceptual row contains a columnar
* object used to control creation/deletion of rows in this table.
*
* 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.
*
* By default, this function returns false, and it is
* assumed that the table has no such control variable.
* When mibgen is used over SMIv2 MIBs, it will generate
* an hasRowStatus() method returning true
* for each table containing an object with RowStatus syntax.
*
* When this method returns false 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).
*
* This method is called internally when a SET request involving
* this table is processed.
*
* 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 true.
* You will then have to redefine the isRowStatus(), mapRowStatus(),
* isRowReady(), and setRowStatus() methods to suit your specific
* implementation.
*
* @return
true if this table contains a control
* variable (eg: a variable with RFC 2579 RowStatus syntax),
*
*
false if this table does not contain
* any control variable.
*
**/
public boolean hasRowStatus() {
return false;
}
// ---------------------------------------------------------------------
//
// Implements the method defined in SnmpMibNode.
//
// ---------------------------------------------------------------------
/**
* Generic handling of the get operation.
*
The default implementation of this method is to
*
*
check whether the entry exists, and if not register an
* exception for each varbind in the list.
*
call the generated
* get(req,oid,depth+1) method.
*
*
*
* 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);
* }
*
*
You should not need to override this method in any cases, because
* it will eventually call
* get(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry. 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
* get(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry.
*
*
*/
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 = null;
for (Enumeration e= r.getElements(); e.hasMoreElements();) {
var = (SnmpVarBind) e.nextElement();
r.registerGetException(var,noSuchInstanceException);
}
}
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 check operation.
*
The default implementation of this method is to
*
*
check whether a new entry must be created, and if remote
* creation of entries is enabled, create it.
*
call the generated
* check(req,oid,depth+1) method.
*
*
*
* 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);
* }
*
*
You should not need to override this method in any cases, because
* it will eventually call
* check(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry. 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
* check(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry.
*
*
*/
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 set operation.
*
The default implementation of this method is to
* call the generated
* set(req,oid,depth+1) method.
*
*
* 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);
* }
*
*
You should not need to override this method in any cases, because
* it will eventually call
* set(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry. 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
* set(SnmpMibSubRequest req, int depth) on the generated
* derivative of SnmpMibEntry.
*
*
*/
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 SnmpMibTable.
* 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 prefered.
* This function is mainly provided for backward compatibility.
*
*
* @param rowOid The SnmpOid 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
* rowOid, 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 SnmpMibTable.
* Also triggers the addEntryCB() callback of the
* {@link com.sun.jmx.snmp.agent.SnmpTableEntryFactory} interface
* if this node is bound to a factory.
*
*
* @param oid The SnmpOid 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
* rowOid, 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.
*
*
* @param rowOid The SnmpOid 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 rowOid 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.
*
*
* @param rowOid The SnmpOid identifying the table
* row to remove.
*
* @exception SnmpStatusException if the specified entry couldn't
* be removed (if the given rowOid 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.
*
*
* @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.
*
*
* @param rowOid The SnmpOid identifying the
* row to be retrieved.
*
* @return The entry.
*
* @exception SnmpStatusException There is no entry with the specified
* rowOid 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.
*
*
* @param rowOid The SnmpOid 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
* rowOid 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 SnmpMibTable.
*
* 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 null. It all depends whether a non
* null entry was passed to addEntry().
* Otherwise, if it uses the standard way (access the entry directly
* through their standard MBean interface) this array will contain all
* the entries.
*
* @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
* SnmpMibTable.
*
*
* @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.
*/
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