/* |
|
* 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(); |
|
for(java.util.Enumeration<?> h = handbackList.elements(); |
|
h.hasMoreElements(); ) { |
|
Object handback = h.nextElement(); |
|
NotificationFilter filter = f.nextElement(); |
|
if ((filter == null) || |
|
(filter.isNotificationEnabled(notification))) { |
|
listener.handleNotification(notification,handback) ; |
|
} |
|
} |
|
} |
|
} |
|
/** |
|
* This method is used by the SnmpMibTable to create and send a table |
|
* entry notification to all the listeners registered for this kind of |
|
* notification. |
|
* |
|
* <p> |
|
* @param type The notification type. |
|
* |
|
* @param timeStamp The notification emission date. |
|
* |
|
* @param entry The entry object. |
|
*/ |
|
private void sendNotification(String type, long timeStamp, |
|
Object entry, ObjectName name) { |
|
synchronized(this) { |
|
sequenceNumber = sequenceNumber + 1; |
|
} |
|
SnmpTableEntryNotification notif = |
|
new SnmpTableEntryNotification(type, this, sequenceNumber, |
|
timeStamp, entry, name); |
|
this.sendNotification(notif) ; |
|
} |
|
/** |
|
* Return true if the entry identified by the given OID index |
|
* is contained in this table. |
|
* <p> |
|
* <b>Do not call this method directly</b>. |
|
* <p> |
|
* This method is provided has a hook for subclasses. |
|
* It is called when a get/set request is received in order to |
|
* determine whether the specified entry is contained in the table. |
|
* You may want to override this method if you need to perform e.g. |
|
* lazy evaluation of tables (you need to update the table when a |
|
* request is received) or if your table is virtual. |
|
* <p> |
|
* Note that this method is called by the Runtime from within a |
|
* synchronized block. |
|
* |
|
* @param oid The index part of the OID we're looking for. |
|
* @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 entry is found, <code>false</code> |
|
* otherwise. |
|
* |
|
* @since 1.5 |
|
**/ |
|
protected boolean contains(SnmpOid oid, Object userData) { |
|
return (findObject(oid) > -1); |
|
} |
|
/** |
|
* Look for the given oid in the OID table (tableoids) and returns |
|
* its position. |
|
* |
|
* <p> |
|
* @param oid The OID we're looking for. |
|
* |
|
* @return The position of the OID in the table. -1 if the given |
|
* OID was not found. |
|
* |
|
**/ |
|
private int findObject(SnmpOid oid) { |
|
int low= 0; |
|
int max= size - 1; |
|
SnmpOid pos; |
|
int comp; |
|
int curr= low + (max-low)/2; |
|
//System.out.println("Try to retrieve: " + oid.toString()); |
|
while (low <= max) { |
|
// XX pos = (SnmpOid) oids.elementAt(curr); |
|
pos = tableoids[curr]; |
|
//System.out.println("Compare with" + pos.toString()); |
|
// never know ...we might find something ... |
|
// |
|
comp = oid.compareTo(pos); |
|
if (comp == 0) |
|
return curr; |
|
if (oid.equals(pos) == true) { |
|
return curr; |
|
} |
|
if (comp > 0) { |
|
low = curr + 1; |
|
} else { |
|
max = curr - 1; |
|
} |
|
curr = low + (max-low)/2; |
|
} |
|
return -1; |
|
} |
|
/** |
|
* Search the position at which the given oid should be inserted |
|
* in the OID table (tableoids). |
|
* |
|
* <p> |
|
* @param oid The OID we would like to insert. |
|
* |
|
* @param fail Tells whether a SnmpStatusException must be generated |
|
* if the given OID is already present in the table. |
|
* |
|
* @return The position at which the OID should be inserted in |
|
* the table. When the OID is found, it returns the next |
|
* position. Note that it is not valid to insert twice the |
|
* same OID. This feature is only an optimization to improve |
|
* the getNextOid() behaviour. |
|
* |
|
* @exception SnmpStatusException if the OID is already present in the |
|
* table and <code>fail</code> is <code>true</code>. |
|
* |
|
**/ |
|
private int getInsertionPoint(SnmpOid oid, boolean fail) |
|
throws SnmpStatusException { |
|
final int failStatus = SnmpStatusException.snmpRspNotWritable; |
|
int low= 0; |
|
int max= size - 1; |
|
SnmpOid pos; |
|
int comp; |
|
int curr= low + (max-low)/2; |
|
while (low <= max) { |
|
// XX pos= (SnmpOid) oids.elementAt(curr); |
|
pos= tableoids[curr]; |
|
// never know ...we might find something ... |
|
// |
|
comp= oid.compareTo(pos); |
|
if (comp == 0) { |
|
if (fail) |
|
throw new SnmpStatusException(failStatus,curr); |
|
else |
|
return curr+1; |
|
} |
|
if (comp>0) { |
|
low= curr +1; |
|
} else { |
|
max= curr -1; |
|
} |
|
curr= low + (max-low)/2; |
|
} |
|
return curr; |
|
} |
|
/** |
|
* Remove the OID located at the given position. |
|
* |
|
* <p> |
|
* @param pos The position at which the OID to be removed is located. |
|
* |
|
**/ |
|
private void removeOid(int pos) { |
|
if (pos >= tablecount) return; |
|
if (pos < 0) return; |
|
final int l1 = --tablecount-pos; |
|
tableoids[pos] = null; |
|
if (l1 > 0) |
|
java.lang.System.arraycopy(tableoids,pos+1,tableoids,pos,l1); |
|
tableoids[tablecount] = null; |
|
} |
|
/** |
|
* Insert an OID at the given position. |
|
* |
|
* <p> |
|
* @param oid The OID to be inserted in the table |
|
* @param pos The position at which the OID to be added is located. |
|
* |
|
**/ |
|
private void insertOid(int pos, SnmpOid oid) { |
|
if (pos >= tablesize || tablecount == tablesize) { |
|
// Vector must be enlarged |
|
// Save old vector |
|
final SnmpOid[] olde = tableoids; |
|
// Allocate larger vectors |
|
tablesize += Delta; |
|
tableoids = new SnmpOid[tablesize]; |
|
// Check pos validity |
|
if (pos > tablecount) pos = tablecount; |
|
if (pos < 0) pos = 0; |
|
final int l1 = pos; |
|
final int l2 = tablecount - pos; |
|
// Copy original vector up to `pos' |
|
if (l1 > 0) |
|
java.lang.System.arraycopy(olde,0,tableoids,0,l1); |
|
// Copy original vector from `pos' to end, leaving |
|
// an empty room at `pos' in the new vector. |
|
if (l2 > 0) |
|
java.lang.System.arraycopy(olde,l1,tableoids, |
|
l1+1,l2); |
|
} else if (pos < tablecount) { |
|
// Vector is large enough to accommodate one additional |
|
// entry. |
|
// |
|
// Shift vector, making an empty room at `pos' |
|
java.lang.System.arraycopy(tableoids,pos,tableoids, |
|
pos+1,tablecount-pos); |
|
} |
|
// Fill the gap at `pos' |
|
tableoids[pos] = oid; |
|
tablecount++; |
|
} |
|
// ---------------------------------------------------------------------- |
|
// PROTECTED VARIABLES |
|
// ---------------------------------------------------------------------- |
|
/** |
|
* The id of the contained entry object. |
|
* @serial |
|
*/ |
|
protected int nodeId=1; |
|
/** |
|
* The MIB to which the metadata is linked. |
|
* @serial |
|
*/ |
|
protected SnmpMib theMib; |
|
/** |
|
* <CODE>true</CODE> if remote creation of entries via SET operations |
|
* is enabled. |
|
* [default value is <CODE>false</CODE>] |
|
* @serial |
|
*/ |
|
protected boolean creationEnabled = false; |
|
/** |
|
* The entry factory |
|
*/ |
|
protected SnmpTableEntryFactory factory = null; |
|
// ---------------------------------------------------------------------- |
|
// PRIVATE VARIABLES |
|
// ---------------------------------------------------------------------- |
|
/** |
|
* The number of elements in the table. |
|
* @serial |
|
*/ |
|
private int size=0; |
|
/** |
|
* The list of indexes. |
|
* @serial |
|
*/ |
|
// private Vector indexes= new Vector(); |
|
/** |
|
* The list of OIDs. |
|
* @serial |
|
*/ |
|
// private Vector oids= new Vector(); |
|
private final static int Delta = 16; |
|
private int tablecount = 0; |
|
private int tablesize = Delta; |
|
private SnmpOid tableoids[] = new SnmpOid[tablesize]; |
|
/** |
|
* The list of entries. |
|
* @serial |
|
*/ |
|
private final Vector<Object> entries= new Vector<>(); |
|
/** |
|
* The list of object names. |
|
* @serial |
|
*/ |
|
private final Vector<ObjectName> entrynames= new Vector<>(); |
|
/** |
|
* Callback handlers |
|
*/ |
|
// final Vector callbacks = new Vector(); |
|
/** |
|
* Listener hashtable containing the hand-back objects. |
|
*/ |
|
private Hashtable<NotificationListener, Vector<Object>> handbackTable = |
|
new Hashtable<>(); |
|
/** |
|
* Listener hashtable containing the filter objects. |
|
*/ |
|
private Hashtable<NotificationListener, Vector<NotificationFilter>> |
|
filterTable = new Hashtable<>(); |
|
// PACKAGE VARIABLES |
|
//------------------ |
|
/** |
|
* SNMP table sequence number. |
|
* The default value is set to 0. |
|
*/ |
|
transient long sequenceNumber = 0; |
|
} |