/*
* @(#)file SnmpInformRequest.java
* @(#)author Sun Microsystems, Inc.
* @(#)version 1.18
* @(#)date 09/10/11
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*/
package com.sun.jmx.snmp.daemon ;
// JAVA imports
//
import java.io.Serializable;
import java.net.InetAddress;
import java.util.Vector;
import java.util.Date;
// JMX imports
//
import com.sun.jmx.snmp.SnmpMessage;
import com.sun.jmx.snmp.SnmpVarBind;
import com.sun.jmx.snmp.SnmpPduFactory;
import com.sun.jmx.snmp.SnmpPduPacket;
import com.sun.jmx.snmp.SnmpPduRequest;
import com.sun.jmx.snmp.SnmpPduBulk;
import com.sun.jmx.snmp.SnmpDefinitions;
import com.sun.jmx.snmp.SnmpStatusException;
import com.sun.jmx.snmp.SnmpTooBigException;
import com.sun.jmx.snmp.SnmpVarBindList;
import com.sun.jmx.snmp.SnmpPdu;
import com.sun.jmx.snmp.SnmpPduRequestType;
// SNMP Runtime imports
//
import com.sun.jmx.trace.Trace;
/**
* This class is used by the {@link com.sun.jmx.snmp.daemon.SnmpAdaptorServer SNMP adaptor server} to send inform requests
* to an SNMP manager and receive inform responses.
* <P>
* This class provides basic functions that enable you to fire inform requests,
* handle retries, timeouts, and process responses from the manager.
* <BR>
* The SNMP adaptor server specifies the destination of the inform request and controls
* the size of a single inform request/response to fit into its <CODE>bufferSize</CODE>.
* It specifies the maximum number of tries and the timeout to be used for the inform requests.
* It also provides resources such as the authentication mechanism (using its PDU factory),
* controlling all inform requests created by it, and finally the inform response to the user.
* <P>
* Each inform request, when ready to be sent, is assigned a unique identifier which helps
* in identifying the inform request with matching inform responses to the protocol engine
* lying transparently underneath. The engine does the job of retrying the inform requests
* when the timer expires and calls the SNMP adaptor server when a timeout occurs after exhausting
* the maximum number of tries.
* <P>
* The inform request object provides the method, {@link #waitForCompletion waitForCompletion(long time)},
* which enables a user to operate in a synchronous mode with an inform request.
* This is done by blocking the user thread for the desired time interval.
* The user thread gets notified whenever a request reaches completion, independently of the status of the response.
* <P>
* If an {@link com.sun.jmx.snmp.daemon.SnmpInformHandler inform callback} is provided when sending the inform request,
* the user operates in an asynchronous mode with the inform request. The user thread is not blocked
* and the specific inform callback implementation provided by the user is invoked when the inform response is received.
*
* <P>
* <B>Note:</B>
* <BR>From RFC 1905, the SNMP inform request is defined as a request generated and transmitted
* by an SNMPv2 entity acting in a manager role to another SNMPv2 entity also acting in a manager role.
* The mechanisms to implement this behaviour are defined in the SNMP manager API.
* <BR>
* Nevertheless, this feature has derived and in some documentations, the inform request appears
* like an SNMPv2 trap that gets responded.
* <BR>The <CODE>SnmpInformRequest</CODE> class is used to fullfill this latter case.
* <p><b>This API is a Sun Microsystems internal API and is subject
* to change without notice.</b></p>
*/
public class SnmpInformRequest implements SnmpDefinitions {
// VARIABLES
//----------
/**
* This object maintains a global counter for the inform request ID.
*/
private static SnmpRequestCounter requestCounter = new SnmpRequestCounter();
/**
* This contains a list of <CODE>SnmpVarBind</CODE> objects for making the SNMP inform requests.
*/
private SnmpVarBindList varBindList = null;
/**
* The error status associated with the inform response packet.
*/
int errorStatus = 0;
/**
* The index in <CODE>SnmpVarBindList</CODE> that caused the exception.
*/
int errorIndex = 0;
//private SnmpVarBind internalVarBind[] = null;
SnmpVarBind internalVarBind[] = null;
//private String reason = null;
String reason = null;
/**
* The SNMP adaptor associated with this inform request.
*/
private transient SnmpAdaptorServer adaptor;
/**
* The session object associated with this inform request.
*/
private transient SnmpSession informSession;
/**
* The user implementation of the callback interface for this request.
*/
private SnmpInformHandler callback = null;
/**
* The inform request PDU.
*/
//private SnmpPduPacket requestPdu;
SnmpPdu requestPdu;
/**
* The inform response PDU.
*/
//private SnmpPduRequest responsePdu;
SnmpPduRequestType responsePdu;
/**
* Base status of an inform request.
*/
final static private int stBase = 1;
/**
* Status of an inform request: in progress.
*/
final static public int stInProgress = stBase;
/**
* Status of an inform request: waiting to be sent.
*/
final static public int stWaitingToSend = (stBase << 1) | stInProgress;
/**
* Status of an inform request: waiting for reply.
*/
final static public int stWaitingForReply = (stBase << 2) | stInProgress;
/**
* Status of an inform request: reply received.
*/
final static public int stReceivedReply = (stBase << 3) | stInProgress;
/**
* Status of an inform request: request aborted.
*/
final static public int stAborted = (stBase << 4);
/**
* Status of an inform request: timeout.
*/
final static public int stTimeout = (stBase << 5);
/**
* Status of an inform request: internal error occured.
*/
final static public int stInternalError = (stBase << 6);
/**
* Status of an inform request: result available for the request.
*/
final static public int stResultsAvailable = (stBase << 7);
/**
* Status of an inform request: request never used.
*/
final static public int stNeverUsed = (stBase << 8);
/**
* Number of tries performed for the current polling operation.
*/
private int numTries = 0;
/**
* Timeout.
* The default amount of time is 3000 millisec.
*/
private int timeout = 3 * 1000; // 3 seconds.
/**
*/
private int reqState = stNeverUsed;
// Polling control parameters.
private long prevPollTime = 0; // value of 0 means poll never happened.
private long nextPollTime = 0;
private long waitTimeForResponse;
private Date debugDate = new Date();
/**
* The request ID for an active inform request.
*/
private int requestId = 0;
private int port = 0;
private InetAddress address = null;
private String communityString = null;
String dbgTag = "SnmpInformRequest";
// CONSTRUCTORS
//-------------
/**
* For SNMP Runtime internal use only.
* Constructor for creating new inform request. This object can be created only by an SNMP adaptor object.
* @param session <CODE>SnmpSession</CODE> object for this inform request.
* @param adp <CODE>SnmpAdaptorServer</CODE> object for this inform request.
* @param addr The <CODE>InetAddress</CODE> destination for this inform request.
* @param cs The community string to be used for the inform request.
* @param requestCB Callback interface for the inform request.
* @exception SnmpStatusException SNMP adaptor is not ONLINE or session is dead.
*/
SnmpInformRequest(SnmpSession session,
SnmpAdaptorServer adp,
InetAddress addr,
String cs,
int p,
SnmpInformHandler requestCB)
throws SnmpStatusException {
informSession = session;
adaptor = adp;
address = addr;
communityString = cs;
port = p;
callback = requestCB;
informSession.addInformRequest(this); // add to adaptor queue.
setTimeout(adaptor.getTimeout()) ;
}
// PUBLIC METHODS
//---------------
/**
* Gets the request id (invoke identifier) of the current inform request.
* @return The request id.
*/
final public synchronized int getRequestId () {
return requestId;
}
/**
* Gets the destination address of the current inform request.
* @return The destination address.
*/
synchronized InetAddress getAddress() {
return address;
}
/**
* Gets the current status of the inform request.
* @return The current status of the inform request.
*/
final public synchronized int getRequestStatus() {
return reqState ;
}
/**
* Indicates whether or not the inform request was aborted.
* @return <CODE>true</CODE> if the inform request was aborted, <CODE>false</CODE> otherwise.
*/
final public synchronized boolean isAborted() {
return ((reqState & stAborted) == stAborted);
}
/**
* Indicates whether or not the inform request is in progress.
* @return <CODE>true</CODE> if the inform request is in progress, <CODE>false</CODE> otherwise.
*/
final public synchronized boolean inProgress() {
return ((reqState & stInProgress) == stInProgress);
}
/**
* Indicates whether or not the inform request result is available.
* @return <CODE>true</CODE> if the inform request result is available, <CODE>false</CODE> otherwise.
*/
final public synchronized boolean isResultAvailable() {
return (reqState == stResultsAvailable);
}
/**
* Gets the status associated with the <CODE>SnmpVarBindList</CODE>.
* @return The error status.
*/
final public synchronized int getErrorStatus() {
return errorStatus;
}
/**
* Gets the index.
* <P>NOTE: this value is equal to the <CODE>errorIndex</CODE> field minus 1.
* @return The error index.
*/
final public synchronized int getErrorIndex() {
return errorIndex;
}
/**
* Gets the maximum number of tries before declaring that the manager is not responding.
* @return The maximum number of times an inform request should be tried.
*/
final public int getMaxTries() {
return adaptor.getMaxTries();
}
/**
* Gets the number of tries performed for the current inform request.
* @return The number of tries performed.
*/
final public synchronized int getNumTries() {
return numTries ;
}
/**
* For SNMP Runtime internal use only.
*/
final synchronized void setTimeout(int value) {
timeout = value ;
}
/**
* Gets absolute time in milliseconds (based on epoch time) when the next
* polling activity will begin.
* @return The absolute time when polling will begin.
*/
final public synchronized long getAbsNextPollTime () {
return nextPollTime ;
}
/**
* Gets absolute time in milliseconds (based on epoch time) before which an inform
* response is expected from a manager.
* @return The absolute time within which an inform response is expected.
*/
final public synchronized long getAbsMaxTimeToWait() {
if (prevPollTime == 0) {
return System.currentTimeMillis() ; // should never happen.
} else {
return waitTimeForResponse ;
}
}
/**
* Gets the <CODE>SnmpVarBindList</CODE> of the inform response.
* It returns a null value if the inform request is in progress.
* This ensures accidental manipulation does not occur when a request is in progress.
* In case of an error, <CODE>SnmpVarBindList</CODE> is the copy
* of the original <CODE>SnmpVarBindList</CODE> at the time of making the inform request.
* @return The list of <CODE>SnmpVarBind</CODE> objects returned by the manager or the null value if the request
* is in progress.
*/
public final synchronized SnmpVarBindList getResponseVarBindList() {
if (inProgress())
return null;
return varBindList;
}
/**
* Used in synchronous mode only.
* Provides a hook that enables a synchronous operation on a previously sent inform request.
* Only one inform request can be in synchronous mode on a given thread.
* The blocked thread is notified when the inform request state reaches completion.
* If the inform request is not active, the method returns immediately.
* The user must get the error status of the inform request to determine the
* exact status of the request.
*
* @param time The amount of time to wait. Zero means block until complete.
* @return <CODE>true</CODE> if the inform request has completed, <CODE>false</CODE> if it is still active.
*/
final public boolean waitForCompletion(long time) {
if (! inProgress()) // check if request is in progress.
return true;
if (informSession.thisSessionContext()) {
// We can manipulate callback safely as we are in session thread.
//
SnmpInformHandler savedCallback = callback;
callback = null;
informSession.waitForResponse(this, time);
callback = savedCallback;
} else {
// This is being done from a different thread. So notifyClient will do the notification.
//
synchronized (this) {
SnmpInformHandler savedCallback = callback ;
try {
callback = null ;
this.wait(time) ;
} catch (InterruptedException e) {
}
callback = savedCallback ;
}
}
return (! inProgress()); // true if request completed.
}
/**
* Cancels the active inform request and removes itself from the polling list.
*/
final public void cancelRequest() {
errorStatus = snmpReqAborted;
stopRequest();
deleteRequest();
notifyClient();
}
/**
* Notifies the registered client about the completion of an operation.
*/
final public synchronized void notifyClient() {
this.notifyAll();
}
/**
* Finalizer of the <CODE>SnmpInformRequest</CODE> objects.
* This method is called by the garbage collector on an object
* when garbage collection determines that there are no more references to the object.
* <P>Sets all the references to this SNMP inform request object to <CODE>null</CODE>.
*/
public void finalize() {
callback = null;
varBindList = null;
internalVarBind = null;
adaptor = null;
informSession = null;
requestPdu = null;
responsePdu = null;
}
/**
* Returns the <CODE>String</CODE> representation of an error code.
* @param errcode The error code as an integer.
* @return The error code as a <CODE>String</CODE>.
*/
public static String snmpErrorToString(int errcode) {
switch (errcode) {
case snmpRspNoError :
return "noError" ;
case snmpRspTooBig :
return "tooBig" ;
case snmpRspNoSuchName :
return "noSuchName" ;
case snmpRspBadValue :
return "badValue" ;
case snmpRspReadOnly :
return "readOnly" ;
case snmpRspGenErr :
return "genErr" ;
case snmpRspNoAccess :
return "noAccess" ;
case snmpRspWrongType :
return "wrongType" ;
case snmpRspWrongLength :
return "wrongLength" ;
case snmpRspWrongEncoding :
return "wrongEncoding" ;
case snmpRspWrongValue :
return "wrongValue" ;
case snmpRspNoCreation :
return "noCreation" ;
case snmpRspInconsistentValue :
return "inconsistentValue" ;
case snmpRspResourceUnavailable :
return "resourceUnavailable" ;
case snmpRspCommitFailed :
return "commitFailed" ;
case snmpRspUndoFailed :
return "undoFailed" ;
case snmpRspAuthorizationError :
return "authorizationError" ;
case snmpRspNotWritable :
return "notWritable" ;
case snmpRspInconsistentName :
return "inconsistentName" ;
case snmpReqTimeout :
return "reqTimeout" ;
case snmpReqAborted :
return "reqAborted" ;
case snmpRspDecodingError :
return "rspDecodingError" ;
case snmpReqEncodingError :
return "reqEncodingError" ;
case snmpReqPacketOverflow :
return "reqPacketOverflow" ;
case snmpRspEndOfTable :
return "rspEndOfTable" ;
case snmpReqRefireAfterVbFix :
return "reqRefireAfterVbFix" ;
case snmpReqHandleTooBig :
return "reqHandleTooBig" ;
case snmpReqTooBigImpossible :
return "reqTooBigImpossible" ;
case snmpReqInternalError :
return "reqInternalError" ;
case snmpReqSocketIOError :
return "reqSocketIOError" ;
case snmpReqUnknownError :
return "reqUnknownError" ;
case snmpWrongSnmpVersion :
return "wrongSnmpVersion" ;
case snmpUnknownPrincipal:
return "snmpUnknownPrincipal";
case snmpAuthNotSupported:
return "snmpAuthNotSupported";
case snmpPrivNotSupported:
return "snmpPrivNotSupported";
case snmpBadSecurityLevel:
return "snmpBadSecurityLevel";
case snmpUsmBadEngineId:
return "snmpUsmBadEngineId";
case snmpUsmInvalidTimeliness:
return "snmpUsmInvalidTimeliness";
}
return "Unknown Error = " + errcode;
}
// PRIVATE AND PACKAGE METHODS
//----------------------------
/**
* For SNMP Runtime internal use only.
* Starts an inform request in asynchronous mode. The callback interface
* is used to notify the user upon request completion.
* @param vblst The list of <CODE>SnmpVarBind</CODE> to be used.
* @exception SnmpStatusException This inform request is already in progress.
*/
synchronized void start(SnmpVarBindList vblst) throws SnmpStatusException {
if (inProgress())
throw new SnmpStatusException("Inform request already in progress.");
setVarBindList(vblst);
initializeAndFire();
}
private synchronized void initializeAndFire() {
requestPdu = null;
responsePdu = null;
reason = null;
startRequest(System.currentTimeMillis());
setErrorStatusAndIndex(0, 0);
}
/**
* This method submits the inform request for polling and marks the request
* active. It does nothing if the request is already active.
* The poll will be scheduled to happen immediately.
* @param starttime The start time for polling.
*/
private synchronized void startRequest(long starttime) {
nextPollTime = starttime;
prevPollTime = 0;
schedulePoll();
}
/**
* This method creates a new request ID. The ID is submitted to the poll server for scheduling.
*/
private void schedulePoll() {
numTries = 0;
initNewRequest();
setRequestStatus(stWaitingToSend);
informSession.getSnmpQManager().addRequest(this);
}
/**
* This method determines whether the inform request is to be retried. This is used if the
* peer did not respond to a previous request. If the request exceeds
* the maxTries limit, a timeout is signaled.
*/
void action() {
if (inProgress() == false)
return;
while (true) {
try {
if (numTries == 0) {
invokeOnReady();
} else if (numTries < getMaxTries()) {
invokeOnRetry();
} else {
invokeOnTimeout();
}
return ;
} catch (OutOfMemoryError omerr) {
// Consider it as a try !
//
numTries++;
if (isDebugOn()) {
debug("action", "Inform request hit out of memory situation...");
}
Thread.currentThread().yield();
}
}
}
final private void invokeOnReady() {
if (requestPdu == null) {
requestPdu = constructPduPacket();
}
if (requestPdu != null) {
if (sendPdu() == false)
queueResponse();
}
}
final private void invokeOnRetry() {
invokeOnReady();
}
final private void invokeOnTimeout() {
errorStatus = snmpReqTimeout;
queueResponse();
}
final private void queueResponse() {
informSession.addResponse(this);
}
/**
* Constructs an inform request PDU.
*/
synchronized SnmpPdu constructPduPacket() {
SnmpPduPacket reqpdu = null;
Exception excep = null;
try {
reqpdu = new SnmpPduRequest();
reqpdu.port = port;
reqpdu.type = pduInformRequestPdu;
reqpdu.version = snmpVersionTwo;
reqpdu.community = communityString.getBytes("8859_1");
reqpdu.requestId = getRequestId();
reqpdu.varBindList = internalVarBind;
if (isTraceOn()) {
trace("constructPduPacket", "Packet built");
}
} catch (Exception e) {
excep = e;
errorStatus = snmpReqUnknownError;
reason = e.getMessage();
}
if (excep != null) {
if (isDebugOn()) {
debug("constructPduPacket", excep);
}
reqpdu = null;
queueResponse();
}
return reqpdu;
}
boolean sendPdu() {
try {
responsePdu = null;
SnmpPduFactory pduFactory = adaptor.getPduFactory();
SnmpMessage msg = (SnmpMessage)pduFactory.encodeSnmpPdu((SnmpPduPacket)requestPdu, adaptor.getBufferSize().intValue());
if (msg == null) {
if (isDebugOn()) {
debug("sendPdu", "pdu factory returned a null value");
}
throw new SnmpStatusException(snmpReqUnknownError);
// This exception will caught hereafter and reported as an snmpReqUnknownError
// FIXME: may be it's not the best behaviour ?
}
int maxPktSize = adaptor.getBufferSize().intValue();
byte[] encoding = new byte[maxPktSize];
int encodingLength = msg.encodeMessage(encoding);
if (isTraceOn()) {
trace("sendPdu", "Dump : \n" + msg.printMessage());
}
sendPduPacket(encoding, encodingLength);
return true;
} catch (SnmpTooBigException ar) {
if (isDebugOn()) {
debug("sendPdu", ar);
}
setErrorStatusAndIndex(snmpReqPacketOverflow, ar.getVarBindCount());
requestPdu = null;
reason = ar.getMessage();
if (isDebugOn()) {
debug("sendPdu", "Packet Overflow while building inform request");
}
} catch (java.io.IOException ioe) {
setErrorStatusAndIndex(snmpReqSocketIOError, 0);
reason = ioe.getMessage();
} catch (Exception e) {
if (isDebugOn()) {
debug("sendPdu", e);
}
setErrorStatusAndIndex(snmpReqUnknownError, 0);
reason = e.getMessage();
}
return false;
}
/**
* Sends the prepared PDU packet to the manager and updates the data structure
* to expect a response. It acquires a lock on the socket to prevent a case
* where a response arrives before this thread could insert the
* request into the wait queue.
* @exception IOException Signals that an I/O exception of some sort has occurred.
*/
final void sendPduPacket(byte[] buffer, int length) throws java.io.IOException {
if (isTraceOn()) {
trace("sendPduPacket", "Send to peer. Peer/Port : " + address.getHostName() + "/" + port +
". Length = " + length + "\nDump : \n" + SnmpMessage.dumpHexBuffer(buffer,0, length));
}
SnmpSocket theSocket = informSession.getSocket();
synchronized (theSocket) {
theSocket.sendPacket(buffer, length, address, port);
setRequestSentTime(System.currentTimeMillis());
}
}
/**
* For SNMP Runtime internal use only.
*/
final void processResponse() {
if (isTraceOn()) {
trace("processResponse", "errstatus = " + errorStatus);
}
if (inProgress() == false) { // check if this request is still alive.
responsePdu = null;
return; // the request may have cancelled.
}
if (errorStatus >= snmpReqInternalError) {
handleInternalError("Internal Error...");
return;
}
try {
parsePduPacket(responsePdu);
//responsePdu = null;
// At this point the errorIndex is rationalized to start with 0.
switch (errorStatus) {
case snmpRspNoError :
handleSuccess();
return;
case snmpReqTimeout :
handleTimeout();
return;
case snmpReqInternalError :
handleInternalError("Unknown internal error. deal with it later!");
return;
case snmpReqHandleTooBig :
setErrorStatusAndIndex(snmpRspTooBig, 0);
handleError("Cannot handle too-big situation...");
return;
case snmpReqRefireAfterVbFix :
// Refire request after fixing varbindlist.
initializeAndFire();
return;
default :
handleError("Error status set in packet...!!");
return;
}
} catch (Exception e) {
if (isDebugOn()) {
debug("processResponse", e);
}
reason = e.getMessage();
}
handleInternalError(reason);
}
/**
* Parses the inform response packet. If the agent responds with error set,
* it does not parse any further.
*/
synchronized void parsePduPacket(SnmpPduRequestType rpdu) {
if (rpdu == null)
return;
errorStatus = rpdu.getErrorStatus();
errorIndex = rpdu.getErrorIndex();
if (errorStatus == snmpRspNoError) {
updateInternalVarBindWithResult(((SnmpPdu)rpdu).varBindList);
return;
}
if (errorStatus != snmpRspNoError)
--errorIndex; // rationalize for index to start with 0.
if (isTraceOn()) {
trace("parsePduPacket", "received inform response. ErrorStatus/ErrorIndex = " + errorStatus + "/" + errorIndex);
}
}
/**
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
*/
private void handleSuccess() {
setRequestStatus(stResultsAvailable);
if (isTraceOn()) {
trace("handleSuccess", "Invoking user defined callback...");
}
deleteRequest(); // delete only non-poll request.
notifyClient();
requestPdu = null;
//responsePdu = null;
internalVarBind = null;
try { // catch all user exception which may happen in callback.
if (callback != null)
callback.processSnmpPollData(this, errorStatus, errorIndex, getVarBindList());
} catch (Exception e) {
if (isDebugOn()) {
debug("handleSuccess", "Exception generated by user callback");
debug("handleSuccess", e);
}
} catch (OutOfMemoryError ome) {
if (isDebugOn()) {
debug("handleSuccess", "OutOfMemory Error generated by user callback");
debug("handleSuccess", ome);
}
Thread.currentThread().yield();
}
return;
}
/**
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
*/
private void handleTimeout() {
setRequestStatus(stTimeout);
if (isDebugOn()) {
debug("handleTimeout", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
errorIndex + ". Invoking timeout user defined callback...");
}
deleteRequest();
notifyClient();
requestPdu = null;
responsePdu = null;
internalVarBind = null;
try {
if (callback != null)
callback.processSnmpPollTimeout(this);
} catch (Exception e) { // catch any exception a user might not handle.
if (isDebugOn()) {
debug("handleTimeout", "Exception generated by user callback");
debug("handleTimeout", e);
}
} catch (OutOfMemoryError ome) {
if (isDebugOn()) {
debug("handleTimeout", "OutOfMemory Error generated by user callback");
debug("handleTimeout", ome);
}
Thread.currentThread().yield();
}
return;
}
/**
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
*/
private void handleError(String msg) {
setRequestStatus(stResultsAvailable);
if (isDebugOn()) {
debug("handleError", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
errorIndex + ". Invoking error user defined callback...\n" + getVarBindList());
}
deleteRequest();
notifyClient();
requestPdu = null;
responsePdu = null;
internalVarBind = null;
try {
if (callback != null)
callback.processSnmpPollData(this, getErrorStatus(), getErrorIndex(), getVarBindList());
} catch (Exception e) { // catch any exception a user might not handle.
if (isDebugOn()) {
debug("handleError", "Exception generated by user callback");
debug("handleError", e);
}
} catch (OutOfMemoryError ome) {
if (isDebugOn()) {
debug("handleError", "OutOfMemory Error generated by user callback");
debug("handleError", ome);
}
Thread.currentThread().yield();
}
}
/**
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
*/
private void handleInternalError(String msg) {
setRequestStatus(stInternalError);
if (reason == null)
reason = msg;
if (isDebugOn()) {
debug("handleInternalError", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
errorIndex + ". Invoking internal error user defined callback...\n" + getVarBindList());
}
deleteRequest();
notifyClient();
requestPdu = null;
responsePdu = null;
internalVarBind = null;
try {
if (callback != null)
callback.processSnmpInternalError(this, reason);
} catch (Exception e) { // catch any exception a user might not handle.
if (isDebugOn()) {
debug("handleInternalError", "Exception generated by user callback");
debug("handleInternalError", e);
}
} catch (OutOfMemoryError ome) {
if (isDebugOn()) {
debug("handleInternalError", "OutOfMemory Error generated by user callback");
debug("handleInternalError", ome);
}
Thread.currentThread().yield();
}
}
void updateInternalVarBindWithResult(SnmpVarBind[] list) {
if ((list == null) || (list.length == 0))
return;
int idx = 0;
for(int i = 0; i < internalVarBind.length && idx < list.length; i++) {
SnmpVarBind avar = internalVarBind[i];
if (avar == null)
continue;
SnmpVarBind res = list[idx];
avar.setSnmpValue(res.getSnmpValue());
idx++;
}
}
/**
* For SNMP Runtime internal use only.
*/
final void invokeOnResponse(Object resp) {
if (resp != null) {
if (resp instanceof SnmpPduRequestType)
responsePdu = (SnmpPduRequestType) resp;
else
return;
}
setRequestStatus(stReceivedReply);
queueResponse();
}
/**
* This method cancels an active inform request and removes it from the polling list.
*/
private void stopRequest() {
// Remove the clause synchronized of the stopRequest method.
// Synchronization is isolated as possible to avoid thread lock.
// Note: the method removeRequest from SendQ is synchronized.
// fix bug jaw.00392.B
//
synchronized(this) {
setRequestStatus(stAborted);
}
informSession.getSnmpQManager().removeRequest(this);
synchronized(this) {
requestId = 0;
}
}
final synchronized void deleteRequest() {
informSession.removeInformRequest(this);
}
/**
* For SNMP Runtime internal use only.
* Gets the active <CODE>SnmpVarBindList</CODE>. The contents of it
* are not guaranteed to be consistent when the inform request is active.
* @return The list of <CODE>SnmpVarBind</CODE> objects.
*/
final synchronized SnmpVarBindList getVarBindList() {
return varBindList;
}
/**
* For SNMP Runtime internal use only.
* You should specify the <CODE>SnmpVarBindList</CODE> at SnmpInformRequest creation time.
* You cannot modify it during the life-time of the object.
*/
final synchronized void setVarBindList(SnmpVarBindList newvblst) {
varBindList = newvblst;
if (internalVarBind == null || internalVarBind.length != varBindList.size()) {
internalVarBind = new SnmpVarBind[varBindList.size()];
}
varBindList.copyInto(internalVarBind);
}
/**
* For SNMP Runtime internal use only.
*/
final synchronized void setErrorStatusAndIndex(int stat, int idx) {
errorStatus = stat;
errorIndex = idx;
}
/**
* For SNMP Runtime internal use only.
*/
final synchronized void setPrevPollTime(long prev) {
prevPollTime = prev;
}
/**
* For SNMP Runtime internal use only.
*/
final void setRequestSentTime(long sendtime) {
numTries++;
setPrevPollTime(sendtime);
waitTimeForResponse = prevPollTime + timeout*numTries;
setRequestStatus(stWaitingForReply);
if (isTraceOn()) {
trace("setRequestSentTime", "Inform request Successfully sent");
}
informSession.getSnmpQManager().addWaiting(this);
}
/**
* Initializes the request id from the request counter.
*/
final synchronized void initNewRequest() {
requestId = requestCounter.getNewId();
}
/**
* For SNMP Runtime internal use only.
*/
long timeRemainingForAction(long currtime) {
switch (reqState) {
case stWaitingToSend :
return nextPollTime - currtime;
case stWaitingForReply :
return waitTimeForResponse - currtime;
default :
return -1;
}
}
/**
* Returns the string state corresponding to the specified integer state.
* @param state The integer state.
* @return The string state.
*/
final static String statusDescription(int state) {
switch (state) {
case stWaitingToSend :
return "Waiting to send.";
case stWaitingForReply :
return "Waiting for reply.";
case stReceivedReply :
return "Response arrived.";
case stAborted :
return "Aborted by user.";
case stTimeout :
return "Timeout Occured.";
case stInternalError :
return "Internal error.";
case stResultsAvailable :
return "Results available";
case stNeverUsed :
return "Inform request in createAndWait state";
}
return "Unknown inform request state.";
}
/**
* Sets the request status to the specified value.
* @param reqst The new status request.
*/
final synchronized void setRequestStatus(int reqst) {
reqState = reqst;
}
/**
* Gives a status report of the request.
* @return The status report of the request.
*/
public synchronized String toString() {
StringBuffer s = new StringBuffer(300) ;
s.append(tostring()) ;
s.append("\nPeer/Port : " + address.getHostName() + "/" + port) ;
return s.toString() ;
}
private synchronized String tostring() {
StringBuffer s = new StringBuffer("InformRequestId = " + requestId);
s.append(" " + "Status = " + statusDescription(reqState));
s.append(" Timeout/MaxTries/NumTries = " + timeout*numTries + "/" +
+ getMaxTries() + "/" + numTries);
if (prevPollTime > 0) {
debugDate.setTime(prevPollTime);
s.append("\nPrevPolled = " + debugDate.toString());
} else
s.append("\nNeverPolled");
s.append(" / RemainingTime(millis) = " +
timeRemainingForAction(System.currentTimeMillis()));
return s.toString();
}
// TRACES & DEBUG
//---------------
boolean isTraceOn() {
return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_ADAPTOR_SNMP);
}
void trace(String clz, String func, String info) {
Trace.send(Trace.LEVEL_TRACE, Trace.INFO_ADAPTOR_SNMP, clz, func, info);
}
void trace(String func, String info) {
trace(dbgTag, func, info);
}
boolean isDebugOn() {
return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP);
}
void debug(String clz, String func, String info) {
Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP, clz, func, info);
}
void debug(String clz, String func, Throwable exception) {
Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP, clz, func, exception);
}
void debug(String func, String info) {
debug(dbgTag, func, info);
}
void debug(String func, Throwable exception) {
debug(dbgTag, func, exception);
}
}