Architecture - Tables

FriendlySNMP API presents tables declared in MIBs as org.friendlysnmp.FTable class objects.

This class architecture is very similar to a org.friendlysnmp.FScalar class. Refer to Scalar to learn basics.

How and where tables are created
The following discussion uses a table flightEntry declared in DEMO-TABLE-FLIGHT-MIB from a demo application as an example. This table is defined in the MIB as follows:

flightTable OBJECT-TYPE
    SYNTAX      SEQUENCE OF FlightEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION "Table of Flights"
    ::= { demoMIBObjects 13 }

flightEntry OBJECT-TYPE
    SYNTAX      FlightEntry
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION "Row with flight info"
    INDEX { flightIndex }
    ::= { flightTable 1 }

FlightEntry ::= SEQUENCE {
    flightIndex       Unsigned32,
    flightCarrier     DisplayString,
    ...more definitions here...
    flightRowStatus   RowStatus
}

flightIndex OBJECT-TYPE
    SYNTAX      Unsigned32
    MAX-ACCESS  not-accessible
    STATUS      current
    DESCRIPTION "This is a table column: INDEX"
    ::= { flightEntry 2 }

flightCarrier OBJECT-TYPE
    SYNTAX      DisplayString
    MAX-ACCESS  read-create
    STATUS      current
    DESCRIPTION "This is a table column: flight carrier."
    ::= { flightEntry 10 }

...more definitions here...

flightRowStatus OBJECT-TYPE
    SYNTAX      RowStatus
    MAX-ACCESS  read-create
    STATUS      current
    DESCRIPTION "The row status..."
    DEFVAL  { active }
    ::= { flightEntry 40 }

Tools discussed in MIB-to-Java generation convert the MIB file DEMO-TABLE-FLIGHT-MIB into Java class DemoTableFlightMibFriend. This class has the following structure:

public class DemoTableFlightMibFriend extends BaseMib {
    private FTable flightEntry;

    // Columns for table flightEntry
    public final static FColumn COLUMN_FlightCarrier = 
        new FColumn("FlightCarrier",
                DemoTableFlightMib.idxFlightCarrier, 
                DemoTableFlightMib.colFlightCarrier);
    . . .
    public final static FColumn COLUMN_FlightRowStatus = 
        new FColumn("FlightRowStatus",
                DemoTableFlightMib.idxFlightRowStatus, 
                DemoTableFlightMib.colFlightRowStatus);
    . . .
    public FTable getFlightEntry() {
        return flightEntry;
    }
}
The table flightEntry is initialized when object of the class DemoTableFlightMibFriend added to the SNMP agent.

A set of final static FColumn objects are generated as well. The org.friendlysnmp.FColumn objects are used to access column data. The class FColumn is immutable class. It contains a column name which could be used for logging, messages and so on. It also contains column OID index and column sequential index. All these values cannot be altered thus making this class safe to use.

How to access table object
There are two techniques to get access to FTable objects.

Technique 1. Direct access from the MIB object using getters generated for each table. This technique is used most of the time:

DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
. . .
FTable table = mib.getFlightEntry();

Technique 2. Indirect access from the agent object using table OID. This technique is used very rear, usually when a MIB object is hidden, for example when MIB is internal to the agent and is not visible from the outside:

import org.snmp4j.mp.SnmpConstants;
. . .
FriendlyAgent agent = new FriendlyAgent();
. . .
FTable table = agent.getTable(DemoTableFlightMib.oidFlightEntry);

Table internals
The FTable class is a wrapper which hides SNMP4J org.snmp4j.agent.mo.MOTable managed object and org.snmp4j.agent.mo.MOTableModel table model. The class provides persistency layer and mechanism to add / remove listeners. Each table object is associated with immutable identifier org.friendlysnmp.FID. See also FID. The table FID could be accessed but cannot be modified.

Table rows
There is no special class to present a table row object. Instead there is a set of methods in the FTable class to access table cell values.

Rows could be deleted one by one using row FID or all at once.

Rows could be added to the table with specified row FID or with generated next available row FID.

Table cell syntax and value
The FTable class provides transparent cell values access with conversion basic Java objects and primitives to / from SNMP4J specific org.snmp4j.smi.Variable. The conversion is driven by column syntax declared in the MIB and may throw an exception.

Table cell values are accessed with methods which are very similar to JTable.

try {
    table.setValueAt(..);
    . . .
    Object obj = table.getValueAt(..);
    . . .
} catch (FException e) {
    // handle the problem 
}

Exception is thrown when the new value cannot be properly converted into / from internal presentation or for invalid row / column identifiers.

Table persistency
Table persistency makes sense only for tables declared with read-write access in a MIB. The table persistent content is saved into an external to the application persistent storage and the table content is restored from it after the application startup. The table content becomes persistent only after successful explicit SNMP SET request from a MIB browser for non-volatile scalars. See also Persistency.

The full table content is stored in the persistent storage after any table modification requested from a MIB browser. The MIB browser may modify a single cell value, or add / remove a row. Any of these actions marks the table content as modified and the new modified table content becomes persistent.

The table is volatile by default and its content is not stored in the persistent storage. Explicit call table.setVolatile(false) is required to enable table persistency. This call also loads table persistent content from external storage if table persistent content exists. This call does nothing for read-only table.

private DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
. . .
FTable table = mib.getFlightEntry();
table.setVolatile(false); // loads persistent value (if exist)
if (table.isPersistLoaded()) {
    ... use persistent table content ...
} else {
    ... use default table content ...
}

Table GET event and listener
The FTable class supports listening GET events which are triggered by MIB browser GET requests. The following methods in the class allow adding and removing GET listeners:

public void addGetListener(FTableGetListener l)
public void removeGetListener(FTableGetListener l)

GET listener is called when a MIB browser requests a table value. The GET request from the MIB browser is received by the SNMP agent in the application. The SNMP agent keeps collection of all tables as managed objects. The application is responsible to update the table content in the listener's code thus making the table content correctly reflect the application's current internal content associated with the table. This update is required for tables which represent dynamic application values. Example of the code from demo application to update the table:

private DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
private List<FlightRow> lstRows = content of the table
. . .
FTable table = mib.getFlightEntry();
. . .
table.deleteAll(); // remove all content and load new one
for (FlightRow row : lstRows) {
    FID idRow = table.addRow(row.id);
    table.setValueAt(row.sCarrier,    
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightCarrier);
    table.setValueAt(row.nFlightNo,   
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightNumber);
    table.setValueAt(row.sDestination,
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightDestination);
    table.setValueAt(row.sDepartTime, 
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightDepartTime);
    table.setValueAt(row.statusFlight.nStatus, 
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightStatus);
    table.setValueAt(RowStatusTC.active, 
                     idRow, DemoTableFlightMibFriend.COLUMN_FlightRowStatus);
}
See also Events and Listeners.

Table VALIDATE + SET event and listener
The FTable class supports listening SET events which are triggered by MIB browser SET requests. Each SET event is prepended with VALIDATION event. Handling VALIDATION event is optional. It allows rejecting invalid SET request.

The following methods in the class allow adding and removing SET and optional VALIDATE listeners:

public void addValidationListener(FTableValidationListener l)
public void removeValidationListener(FTableValidationListener l)

public void addSetListener(FTableSetListener l)
public void removeSetListener(FTableSetListener l)

VALIDATE listener is called when a MIB browser attempts to SET a new value to the table cell. This listener makes no sense for read-only table and it is not called at all for it. Missing VALIDATE listener for read-write table allows MIB browser to set any new value.

SET listener is called when a MIB browser attempts to SET a new value and VALIDATION listener is missing or it allows the new value. The application is responsible for updating its internal copy of the value associated with the updated table cell and starting to use it.

Example of the listeners code to VALIDATE and SET table cell value:

private DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
. . .
FTable table = mib.getFlightEntry();
. . .
// Validate table cell new value
table.addValidationListener(new FTableValidationListener() {
    public ValueValidation validate(FTable table, Object objNewValue, 
                                    FID idRow, FColumn col, RowAction action) 
    {
        if (new cell value is not good) {
            return ValueValidation.WRONG_VALUE;
        }
        return ValueValidation.SUCCESS;
    }
});

// Set table cell new value
table.addSetListener(new FTableSetListener() {
    public void set(FTable table, FID idRow, FColumn col, RowAction action) {
        // The table object already has all new values set.
        // The application is responsible to start use them.
        . . .
    }
});

Restore default value
Each read-write table over the lifetime has two sources of content: default content which is initially set in the agent and a content which is set from a MIB browser and preserved in a persistency storage. It is very difficult to rollback table content to a default after a new content is set from a MIB browser. The FriendlySNMP API provides an option to remove table content from persistency storage. The access to the tables persistency storage is provided in FRIENDLY-SNMP-MIB. The agent is notified about this action via SET operation. It is agent's responsibility to handle this type of events with the following RESTORE DEFAULTS listeners:

public void addRestoreDefaultListener(FRestoreDefaultListener l)
public void removeRestoreDefaultListener(FRestoreDefaultListener l)

Missing RESTORE DEFAULT listeners is not a disaster for the application if the application is restarted to pick up the new persistency storage state.

Example of the listener code to RESTORE DEFAULT table content:

private DemoTableFlightMibFriend mib = new DemoTableFlightMibFriend();
. . .
mib.getFlightEntry().addRestoreDefaultListener(new FRestoreDefaultListener() {
    public void restoreDefault(FRestoreDefaultEvent ev) {
        ... load default content ...                
    }
});

Static and dynamic table
Static table has a content which never (or very rare) changes during a lifetime of the application. Examples of such tables are application configuration properties, list of classpath entries, list of used libraries, and so on. The content of a static table should be set explicitly without using GET listeners overhead. Application may set value of a static table at startup and do not keep a working copy of this table content. MIB browser GET request is satisfied with a table content which is already set in the table object.

Dynamic table has a content which is changing frequently in the code. Application with read-only table should provide GET listener to handle MIB browser request to access this table. Application with read-write table should provide at minimum GET and SET listeners to handle MIB browser request to access this table.