| /* |
| * 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; |
| } |