001: /*_############################################################################
002: _##
003: _## SNMP4J-Agent - RowStatus.java
004: _##
005: _## Copyright (C) 2005-2007 Frank Fock (SNMP4J.org)
006: _##
007: _## Licensed under the Apache License, Version 2.0 (the "License");
008: _## you may not use this file except in compliance with the License.
009: _## You may obtain a copy of the License at
010: _##
011: _## http://www.apache.org/licenses/LICENSE-2.0
012: _##
013: _## Unless required by applicable law or agreed to in writing, software
014: _## distributed under the License is distributed on an "AS IS" BASIS,
015: _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: _## See the License for the specific language governing permissions and
017: _## limitations under the License.
018: _##
019: _##########################################################################*/
020:
021: package org.snmp4j.agent.mo.snmp;
022:
023: import java.util.*;
024:
025: import org.snmp4j.*;
026: import org.snmp4j.agent.*;
027: import org.snmp4j.agent.mo.*;
028: import org.snmp4j.agent.request.*;
029: import org.snmp4j.smi.*;
030: import org.snmp4j.log.LogAdapter;
031: import org.snmp4j.log.LogFactory;
032:
033: /**
034: * The <code>RowStatus</code> class implements the columnar object TC RowStatus.
035: * The RowStatus textual convention is used to manage the creation and deletion
036: * of conceptual rows, and is used as the value of the SYNTAX clause for the
037: * status column of a conceptual row. See RFC 2579.
038: *
039: * The RowStatus column controls row creation and deletion in SNMP tables with
040: * READ-CREATE maximum access. Since the state of a dynamic row is/may be
041: * important to dependent rows / other objects of an agent, row status change
042: * events can be propagated to other objects through registering
043: * {@link RowStatusListener}s.
044: *
045: * @author Frank Fock
046: * @version 1.0
047: */
048: public class RowStatus extends MOMutableColumn implements
049: MOChangeListener, MOTableRowListener {
050:
051: private static final LogAdapter logger = LogFactory
052: .getLogger(RowStatus.class);
053:
054: public static final int notExistant = 0;
055: public static final int active = 1;
056: public static final int notInService = 2;
057: public static final int notReady = 3;
058: public static final int createAndGo = 4;
059: public static final int createAndWait = 5;
060: public static final int destroy = 6;
061:
062: private OID oid;
063: private int columnIndex;
064:
065: private transient Vector rowStatusListeners;
066:
067: /**
068: * Creates a RowStatus column with the specified column sub-identifier and
069: * maximum access of 'read-create'.
070: * @param columnID
071: * a column sub-identifier.
072: */
073: public RowStatus(int columnID) {
074: super (columnID, SMIConstants.SYNTAX_INTEGER,
075: MOAccessImpl.ACCESS_READ_CREATE);
076: this .addMOValueValidationListener(new RowStatusValidator());
077: }
078:
079: /**
080: * Creates a RowStatus column with the specified column sub-identifier.
081: * @param columnID
082: * a column sub-identifier.
083: * @param access
084: * the maximum access for the RowStatus column (should be READ-CREATE).
085: */
086: public RowStatus(int columnID, MOAccess access) {
087: super (columnID, SMIConstants.SYNTAX_INTEGER, access);
088: this .addMOValueValidationListener(new RowStatusValidator());
089: }
090:
091: /**
092: * Sets the table instance this columnar object is contained in. This method
093: * should be called by {@link MOTable} instance to register the table with
094: * the RowStatus column. When called, this RowStatus registers itself as
095: * {@link MOChangeListener} and {@link MOTableRowListener}.
096: * @param table
097: * the <code>MOTable</code> instance where this column is contained in.
098: */
099: public void setTable(MOTable table) {
100: super .setTable(table);
101: oid = new OID(table.getOID());
102: oid.append(getColumnID());
103: columnIndex = table.getColumnIndex(getColumnID());
104:
105: table.addMOChangeListener(this );
106: table.addMOTableRowListener(this );
107: }
108:
109: /**
110: * Unsets the table instance and thus unregisters itself as
111: * {@link MOChangeListener} and {@link MOTableRowListener}.
112: * @param table
113: * the <code>MOTable</code> instance where this column was part of.
114: */
115: public void unsetTable(MOTable table) {
116: columnIndex = 0;
117: table.removeMOChangeListener(this );
118: table.removeMOTableRowListener(this );
119: }
120:
121: protected boolean isReady(MOTableRow row, int rowStatusColumn) {
122: return isReady(row, rowStatusColumn, null);
123: }
124:
125: protected boolean isReady(MOTableRow row, int rowStatusColumn,
126: MOTableRow changeSet) {
127: MOColumn[] columns = getTable().getColumns();
128: for (int i = 0; i < columns.length; i++) {
129: if (i == rowStatusColumn) {
130: continue;
131: }
132: if (columns[i] instanceof MOMutableColumn) {
133: MOMutableColumn mcol = (MOMutableColumn) columns[i];
134: if ((mcol.isMandatory())
135: && ((row.getValue(i) == null) && ((changeSet == null) || (changeSet
136: .getValue(i) == null)))) {
137: if (logger.isDebugEnabled()) {
138: logger.debug("Row " + row
139: + " is not ready because column " + i
140: + " is not set properly");
141: }
142: return false;
143: }
144: }
145: }
146: return true;
147: }
148:
149: public void prepare(SubRequest subRequest, MOTableRow row,
150: MOTableRow changeSet, int column) {
151: super .prepare(subRequest, row, null, column);
152: if (subRequest.getStatus().getErrorStatus() == PDU.noError) {
153: int newValue = ((Integer32) subRequest.getVariableBinding()
154: .getVariable()).getValue();
155: int oldValue = notExistant;
156: if (row.getValue(column) != null) {
157: oldValue = ((Integer32) row.getValue(column))
158: .getValue();
159: }
160: if ((oldValue == notExistant) || (oldValue == notReady)
161: || (oldValue == createAndGo)) {
162: switch (newValue) {
163: case createAndGo:
164: case notInService:
165: case active: {
166: if (!isReady(row, column, changeSet)) {
167: if (logger.isDebugEnabled()) {
168: logger
169: .debug(toString()
170: + ": Row '"
171: + row.getIndex()
172: + " is not ready! Cannot change status to from "
173: + oldValue + " to "
174: + newValue);
175: }
176: subRequest.getStatus().setErrorStatus(
177: PDU.inconsistentValue);
178: }
179: break;
180: }
181: }
182: }
183: RowStatusEvent rowStatusEvent = new RowStatusEvent(this ,
184: getTable(), row, changeSet, oldValue, newValue,
185: true);
186: fireRowStatusChanged(rowStatusEvent);
187: if (rowStatusEvent.getDenyReason() != PDU.noError) {
188: subRequest.getStatus().setErrorStatus(
189: rowStatusEvent.getDenyReason());
190: }
191: }
192: }
193:
194: public void commit(SubRequest subRequest, MOTableRow row,
195: MOTableRow changeSet, int column) {
196: int oldValue = ((Integer32) row.getValue(column)).getValue();
197: super .commit(subRequest, row, null, column);
198: if (!subRequest.hasError()) {
199: int newValue = ((Integer32) row.getValue(column))
200: .getValue();
201: assignNewValue(subRequest, row, column, newValue);
202: RowStatusEvent rowStatusEvent = new RowStatusEvent(this ,
203: getTable(), row, changeSet, oldValue, newValue);
204: fireRowStatusChanged(rowStatusEvent);
205: }
206: }
207:
208: protected void assignNewValue(SubRequest subRequest,
209: MOTableRow row, int column, int newValue) {
210: switch (newValue) {
211: case destroy: {
212: MOTableRow deleted = getTable().removeRow(row.getIndex());
213: if (deleted == null) {
214: subRequest.getStatus().setErrorStatus(PDU.commitFailed);
215: } else {
216: subRequest.setUndoValue(deleted);
217: }
218: break;
219: }
220: case createAndWait: {
221: if (isReady(row, column)) {
222: ((Integer32) row.getValue(column))
223: .setValue(RowStatus.notInService);
224: } else {
225: ((Integer32) row.getValue(column))
226: .setValue(RowStatus.notReady);
227: }
228: break;
229: }
230: case createAndGo: {
231: ((Integer32) row.getValue(column))
232: .setValue(RowStatus.active);
233: break;
234: }
235: }
236: }
237:
238: public void undo(SubRequest subRequest, MOTableRow row, int column) {
239: super .undo(subRequest, row, column);
240: if (!subRequest.hasError()) {
241: int newStatus = ((Integer32) row.getValue(column))
242: .getValue();
243: switch (newStatus) {
244: case destroy: {
245: MOTableRow oldRow = (MOTableRow) subRequest
246: .getUndoValue();
247: Integer32 oldValue = (Integer32) oldRow
248: .getValue(column);
249: boolean added = getTable().addRow(
250: (MOTableRow) subRequest.getUndoValue());
251: if (!added) {
252: subRequest.getStatus().setErrorStatus(
253: PDU.undoFailed);
254: } else {
255: RowStatusEvent rowStatusEvent = new RowStatusEvent(
256: this , getTable(), oldRow, row, oldValue
257: .getValue(), RowStatus.destroy);
258: fireRowStatusChanged(rowStatusEvent);
259: }
260: break;
261: }
262: case createAndGo:
263: case createAndWait: {
264: MOTableRow deleted = getTable().removeRow(
265: row.getIndex());
266: if (deleted == null) {
267: subRequest.getStatus().setErrorStatus(
268: PDU.undoFailed);
269: } else {
270: RowStatusEvent rowStatusEvent = new RowStatusEvent(
271: this , getTable(), row, deleted,
272: RowStatus.notExistant, newStatus);
273: fireRowStatusChanged(rowStatusEvent);
274: }
275: break;
276: }
277: }
278: }
279: }
280:
281: public void beforePrepareMOChange(MOChangeEvent changeEvent) {
282: if (changeEvent.getOID().startsWith(oid)) {
283: int currentValue = notExistant;
284: if (changeEvent.getOldValue() instanceof Integer32) {
285: currentValue = ((Integer32) changeEvent.getOldValue())
286: .getValue();
287: }
288: int newValue = ((Integer32) changeEvent.getNewValue())
289: .getValue();
290: boolean ok = false;
291: switch (currentValue) {
292: case notExistant:
293: ok = ((newValue == createAndGo)
294: || (newValue == createAndWait) || (newValue == destroy));
295: break;
296: case notReady:
297: ok = ((newValue == destroy) || (newValue == active) || (newValue == notInService));
298: break;
299: case active:
300: ok = ((newValue == active)
301: || (newValue == notInService) || (newValue == destroy));
302: break;
303: case notInService:
304: ok = ((newValue == notInService)
305: || (newValue == active) || (newValue == destroy));
306: break;
307: // for row creation
308: case createAndWait:
309: ok = (newValue == createAndWait);
310: break;
311: case createAndGo:
312: ok = (newValue == createAndGo);
313: break;
314: case destroy:
315: ok = (newValue == destroy);
316: break;
317: }
318: if (!ok) {
319: changeEvent.setDenyReason(PDU.wrongValue);
320: }
321: }
322: }
323:
324: public void beforeMOChange(MOChangeEvent changeEvent) {
325: }
326:
327: public void afterMOChange(MOChangeEvent changeEvent) {
328: }
329:
330: public void afterPrepareMOChange(MOChangeEvent changeEvent) {
331: }
332:
333: public synchronized void addRowStatusListener(RowStatusListener l) {
334: if (rowStatusListeners == null) {
335: rowStatusListeners = new Vector(2);
336: }
337: rowStatusListeners.add(l);
338: }
339:
340: public synchronized void removeRowStatusListener(RowStatusListener l) {
341: if (rowStatusListeners != null) {
342: rowStatusListeners.remove(l);
343: }
344: }
345:
346: protected void fireRowStatusChanged(RowStatusEvent event) {
347: if (rowStatusListeners != null) {
348: Vector listeners = rowStatusListeners;
349: int count = listeners.size();
350: for (int i = 0; i < count; i++) {
351: ((RowStatusListener) listeners.elementAt(i))
352: .rowStatusChanged(event);
353: }
354: }
355: }
356:
357: /**
358: * Tests if the specified row is active.
359: * @param row
360: * a row with a RowStatus column.
361: * @param rowStatusColumnIndex
362: * the column index of the RowStatus column in <code>row</code>.
363: * @return
364: * <code>true</code> if <code>row</code> is active.
365: */
366: public static boolean isRowActive(MOTableRow row,
367: int rowStatusColumnIndex) {
368: Integer32 rowStatus = (Integer32) row
369: .getValue(rowStatusColumnIndex);
370: if (rowStatus != null) {
371: return rowStatus.getValue() == RowStatus.active;
372: }
373: return false;
374: }
375:
376: /**
377: * The <code>ActiveRowsFilter</code> is a {@link MOTableRowFilter} that
378: * returns only the active rows of a table with a RowStatus column.
379: *
380: * @author Frank Fock
381: * @version 1.0
382: */
383: public static class ActiveRowsFilter implements MOTableRowFilter {
384:
385: private int rowStatusColumnIndex;
386:
387: /**
388: * Creates an active row filter by specifying the RowStatus column's index
389: * in the target table.
390: * @param rowStatusColumnIndex
391: * the column index (zero-based) of the RowStatus column on behalf the
392: * filtering is done.
393: */
394: public ActiveRowsFilter(int rowStatusColumnIndex) {
395: this .rowStatusColumnIndex = rowStatusColumnIndex;
396: }
397:
398: public boolean passesFilter(MOTableRow row) {
399: return (((Integer32) row.getValue(rowStatusColumnIndex))
400: .getValue() == active);
401: }
402: }
403:
404: static class RowStatusValidator implements
405: MOValueValidationListener {
406:
407: public void validate(MOValueValidationEvent event) {
408: if (!(event.getNewValue() instanceof Integer32)) {
409: event.setValidationStatus(PDU.wrongType);
410: }
411: int v = ((Integer32) event.getNewValue()).getValue();
412: if ((v < 1) || (v > 6) || (v == 3)) {
413: event.setValidationStatus(PDU.wrongValue);
414: }
415: }
416: }
417:
418: public void rowChanged(MOTableRowEvent event) {
419: switch (event.getType()) {
420: case MOTableRowEvent.CREATE: {
421: // by default do not allow row creation if RowStatus is not set!
422: MOTableRow row = event.getRow();
423: int myIndex = getTable().getColumnIndex(getColumnID());
424: if (row.getValue(myIndex) == null) {
425: event.setVetoStatus(PDU.inconsistentName);
426: }
427: break;
428: }
429: case MOTableRowEvent.CHANGE: {
430: // check whether the changed column can be changed if row is active
431: int rowStatus = getCurrentRowStatus(event);
432: switch (rowStatus) {
433: case active: {
434: for (int i = 0; i < getTable().getColumnCount(); i++) {
435: if (event.getPreparedChanges().getValue(i) == null) {
436: continue;
437: }
438: MOColumn col = getTable().getColumn(i);
439: if (col instanceof MOMutableColumn) {
440: if (!((MOMutableColumn) col)
441: .isMutableInService()) {
442: event.setVetoStatus(PDU.inconsistentValue);
443: event.setVetoColumn(i);
444: }
445: }
446: }
447: break;
448: }
449: }
450: }
451: case MOTableRowEvent.UPDATED: {
452: // check whether changed row is ready to be set active
453: int rowStatus = getCurrentRowStatus(event);
454: switch (rowStatus) {
455: case notReady: {
456: if ((event.getRow() instanceof MOMutableTableRow)
457: && (isReady(event.getRow(), columnIndex))) {
458: ((MOMutableTableRow) event.getRow()).setValue(
459: columnIndex, new Integer32(notInService));
460: }
461: break;
462: }
463: }
464: }
465: }
466: }
467:
468: private int getCurrentRowStatus(MOTableRowEvent event) {
469: Integer32 rowStatusVariable = (Integer32) event.getRow()
470: .getValue(columnIndex);
471: int rowStatus = RowStatus.notExistant;
472: if (rowStatusVariable != null) {
473: rowStatus = rowStatusVariable.getValue();
474: }
475: return rowStatus;
476: }
477:
478: public boolean isVolatile(MOTableRow row, int column) {
479: Integer32 value = (Integer32) row.getValue(column);
480: if (value != null) {
481: int rowStatus = value.getValue();
482: if ((rowStatus != active) && (rowStatus != notInService)) {
483: return true;
484: }
485: }
486: return false;
487: }
488:
489: public void get(SubRequest subRequest, MOTableRow row, int column) {
490: Integer32 rowStatus = (Integer32) getValue(row, column);
491: if ((rowStatus != null) && (rowStatus.getValue() == notReady)) {
492: if (isReady(row, column)) {
493: rowStatus.setValue(notInService);
494: }
495: }
496: super.get(subRequest, row, column);
497: }
498:
499: }
|