You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1177 lines
40 KiB
1177 lines
40 KiB
/*
|
|
*
|
|
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
// Copyright (c) 1995-96 by Cisco Systems, Inc.
|
|
|
|
package com.sun.jmx.snmp.daemon;
|
|
|
|
// JAVA imports
|
|
//
|
|
import java.net.InetAddress;
|
|
import java.util.Date;
|
|
import java.util.logging.Level;
|
|
|
|
// JMX imports
|
|
//
|
|
import static com.sun.jmx.defaults.JmxProperties.SNMP_ADAPTOR_LOGGER;
|
|
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.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;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
// 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>.
|
|
*/
|
|
@Override
|
|
protected 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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"action", "Inform request hit out of memory situation...");
|
|
}
|
|
Thread.yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void invokeOnReady() {
|
|
if (requestPdu == null) {
|
|
requestPdu = constructPduPacket();
|
|
}
|
|
if (requestPdu != null) {
|
|
if (sendPdu() == false)
|
|
queueResponse();
|
|
}
|
|
}
|
|
|
|
private void invokeOnRetry() {
|
|
invokeOnReady();
|
|
}
|
|
|
|
private void invokeOnTimeout() {
|
|
errorStatus = snmpReqTimeout;
|
|
queueResponse();
|
|
}
|
|
|
|
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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"constructPduPacket", "Packet built");
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
excep = e;
|
|
errorStatus = snmpReqUnknownError;
|
|
reason = e.getMessage();
|
|
}
|
|
if (excep != null) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"constructPduPacket", "Got unexpected exception", 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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"sendPdu", "Dump : \n" + msg.printMessage());
|
|
}
|
|
|
|
sendPduPacket(encoding, encodingLength);
|
|
return true;
|
|
} catch (SnmpTooBigException ar) {
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"sendPdu", "Got unexpected exception", ar);
|
|
}
|
|
|
|
setErrorStatusAndIndex(snmpReqPacketOverflow, ar.getVarBindCount());
|
|
requestPdu = null;
|
|
reason = ar.getMessage();
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"sendPdu", "Packet Overflow while building inform request");
|
|
}
|
|
} catch (java.io.IOException ioe) {
|
|
setErrorStatusAndIndex(snmpReqSocketIOError, 0);
|
|
reason = ioe.getMessage();
|
|
} catch (Exception e) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"sendPdu", "Got unexpected exception", 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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"processResponse", "Got unexpected exception", 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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"parsePduPacket", "received inform response. ErrorStatus/ErrorIndex = "
|
|
+ errorStatus + "/" + errorIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
|
|
*/
|
|
private void handleSuccess() {
|
|
|
|
setRequestStatus(stResultsAvailable);
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleSuccess", "Exception generated by user callback", e);
|
|
}
|
|
} catch (OutOfMemoryError ome) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleSuccess", "OutOfMemory Error generated by user callback", ome);
|
|
}
|
|
Thread.yield();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
|
|
*/
|
|
private void handleTimeout() {
|
|
|
|
setRequestStatus(stTimeout);
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleTimeout", "Exception generated by user callback", e);
|
|
}
|
|
} catch (OutOfMemoryError ome) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleTimeout", "OutOfMemory Error generated by user callback", ome);
|
|
}
|
|
Thread.yield();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
|
|
*/
|
|
private void handleError(String msg) {
|
|
|
|
setRequestStatus(stResultsAvailable);
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleError", "Exception generated by user callback", e);
|
|
}
|
|
} catch (OutOfMemoryError ome) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleError", "OutOfMemory Error generated by user callback", ome);
|
|
}
|
|
Thread.yield();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
|
|
*/
|
|
private void handleInternalError(String msg) {
|
|
|
|
setRequestStatus(stInternalError);
|
|
if (reason == null)
|
|
reason = msg;
|
|
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleInternalError", "Exception generated by user callback", e);
|
|
}
|
|
} catch (OutOfMemoryError ome) {
|
|
if (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINEST)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINEST, SnmpInformRequest.class.getName(),
|
|
"handleInternalError", "OutOfMemory Error generated by user callback", ome);
|
|
}
|
|
Thread.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 (SNMP_ADAPTOR_LOGGER.isLoggable(Level.FINER)) {
|
|
SNMP_ADAPTOR_LOGGER.logp(Level.FINER, SnmpInformRequest.class.getName(),
|
|
"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.
|
|
*/
|
|
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.
|
|
*/
|
|
@Override
|
|
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();
|
|
}
|
|
|
|
|
|
}
|