1. /*
  2. * @(#)file SnmpInformRequest.java
  3. * @(#)author Sun Microsystems, Inc.
  4. * @(#)version 1.17
  5. * @(#)date 04/09/15
  6. *
  7. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  8. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  9. *
  10. */
  11. package com.sun.jmx.snmp.daemon ;
  12. // JAVA imports
  13. //
  14. import java.io.Serializable;
  15. import java.net.InetAddress;
  16. import java.util.Vector;
  17. import java.util.Date;
  18. // JMX imports
  19. //
  20. import com.sun.jmx.snmp.SnmpMessage;
  21. import com.sun.jmx.snmp.SnmpVarBind;
  22. import com.sun.jmx.snmp.SnmpPduFactory;
  23. import com.sun.jmx.snmp.SnmpPduPacket;
  24. import com.sun.jmx.snmp.SnmpPduRequest;
  25. import com.sun.jmx.snmp.SnmpPduBulk;
  26. import com.sun.jmx.snmp.SnmpDefinitions;
  27. import com.sun.jmx.snmp.SnmpStatusException;
  28. import com.sun.jmx.snmp.SnmpTooBigException;
  29. import com.sun.jmx.snmp.SnmpVarBindList;
  30. import com.sun.jmx.snmp.SnmpPdu;
  31. import com.sun.jmx.snmp.SnmpPduRequestType;
  32. // SNMP Runtime imports
  33. //
  34. import com.sun.jmx.trace.Trace;
  35. /**
  36. * This class is used by the {@link com.sun.jmx.snmp.daemon.SnmpAdaptorServer SNMP adaptor server} to send inform requests
  37. * to an SNMP manager and receive inform responses.
  38. * <P>
  39. * This class provides basic functions that enable you to fire inform requests,
  40. * handle retries, timeouts, and process responses from the manager.
  41. * <BR>
  42. * The SNMP adaptor server specifies the destination of the inform request and controls
  43. * the size of a single inform request/response to fit into its <CODE>bufferSize</CODE>.
  44. * It specifies the maximum number of tries and the timeout to be used for the inform requests.
  45. * It also provides resources such as the authentication mechanism (using its PDU factory),
  46. * controlling all inform requests created by it, and finally the inform response to the user.
  47. * <P>
  48. * Each inform request, when ready to be sent, is assigned a unique identifier which helps
  49. * in identifying the inform request with matching inform responses to the protocol engine
  50. * lying transparently underneath. The engine does the job of retrying the inform requests
  51. * when the timer expires and calls the SNMP adaptor server when a timeout occurs after exhausting
  52. * the maximum number of tries.
  53. * <P>
  54. * The inform request object provides the method, {@link #waitForCompletion waitForCompletion(long time)},
  55. * which enables a user to operate in a synchronous mode with an inform request.
  56. * This is done by blocking the user thread for the desired time interval.
  57. * The user thread gets notified whenever a request reaches completion, independently of the status of the response.
  58. * <P>
  59. * If an {@link com.sun.jmx.snmp.daemon.SnmpInformHandler inform callback} is provided when sending the inform request,
  60. * the user operates in an asynchronous mode with the inform request. The user thread is not blocked
  61. * and the specific inform callback implementation provided by the user is invoked when the inform response is received.
  62. *
  63. * <P>
  64. * <B>Note:</B>
  65. * <BR>From RFC 1905, the SNMP inform request is defined as a request generated and transmitted
  66. * by an SNMPv2 entity acting in a manager role to another SNMPv2 entity also acting in a manager role.
  67. * The mechanisms to implement this behaviour are defined in the SNMP manager API.
  68. * <BR>
  69. * Nevertheless, this feature has derived and in some documentations, the inform request appears
  70. * like an SNMPv2 trap that gets responded.
  71. * <BR>The <CODE>SnmpInformRequest</CODE> class is used to fullfill this latter case.
  72. * <p><b>This API is a Sun Microsystems internal API and is subject
  73. * to change without notice.</b></p>
  74. */
  75. public class SnmpInformRequest implements SnmpDefinitions {
  76. // VARIABLES
  77. //----------
  78. /**
  79. * This object maintains a global counter for the inform request ID.
  80. */
  81. private static SnmpRequestCounter requestCounter = new SnmpRequestCounter();
  82. /**
  83. * This contains a list of <CODE>SnmpVarBind</CODE> objects for making the SNMP inform requests.
  84. */
  85. private SnmpVarBindList varBindList = null;
  86. /**
  87. * The error status associated with the inform response packet.
  88. */
  89. int errorStatus = 0;
  90. /**
  91. * The index in <CODE>SnmpVarBindList</CODE> that caused the exception.
  92. */
  93. int errorIndex = 0;
  94. //private SnmpVarBind internalVarBind[] = null;
  95. SnmpVarBind internalVarBind[] = null;
  96. //private String reason = null;
  97. String reason = null;
  98. /**
  99. * The SNMP adaptor associated with this inform request.
  100. */
  101. private transient SnmpAdaptorServer adaptor;
  102. /**
  103. * The session object associated with this inform request.
  104. */
  105. private transient SnmpSession informSession;
  106. /**
  107. * The user implementation of the callback interface for this request.
  108. */
  109. private SnmpInformHandler callback = null;
  110. /**
  111. * The inform request PDU.
  112. */
  113. //private SnmpPduPacket requestPdu;
  114. SnmpPdu requestPdu;
  115. /**
  116. * The inform response PDU.
  117. */
  118. //private SnmpPduRequest responsePdu;
  119. SnmpPduRequestType responsePdu;
  120. /**
  121. * Base status of an inform request.
  122. */
  123. final static private int stBase = 1;
  124. /**
  125. * Status of an inform request: in progress.
  126. */
  127. final static public int stInProgress = stBase;
  128. /**
  129. * Status of an inform request: waiting to be sent.
  130. */
  131. final static public int stWaitingToSend = (stBase << 1) | stInProgress;
  132. /**
  133. * Status of an inform request: waiting for reply.
  134. */
  135. final static public int stWaitingForReply = (stBase << 2) | stInProgress;
  136. /**
  137. * Status of an inform request: reply received.
  138. */
  139. final static public int stReceivedReply = (stBase << 3) | stInProgress;
  140. /**
  141. * Status of an inform request: request aborted.
  142. */
  143. final static public int stAborted = (stBase << 4);
  144. /**
  145. * Status of an inform request: timeout.
  146. */
  147. final static public int stTimeout = (stBase << 5);
  148. /**
  149. * Status of an inform request: internal error occured.
  150. */
  151. final static public int stInternalError = (stBase << 6);
  152. /**
  153. * Status of an inform request: result available for the request.
  154. */
  155. final static public int stResultsAvailable = (stBase << 7);
  156. /**
  157. * Status of an inform request: request never used.
  158. */
  159. final static public int stNeverUsed = (stBase << 8);
  160. /**
  161. * Number of tries performed for the current polling operation.
  162. */
  163. private int numTries = 0;
  164. /**
  165. * Timeout.
  166. * The default amount of time is 3000 millisec.
  167. */
  168. private int timeout = 3 * 1000; // 3 seconds.
  169. /**
  170. */
  171. private int reqState = stNeverUsed;
  172. // Polling control parameters.
  173. private long prevPollTime = 0; // value of 0 means poll never happened.
  174. private long nextPollTime = 0;
  175. private long waitTimeForResponse;
  176. private Date debugDate = new Date();
  177. /**
  178. * The request ID for an active inform request.
  179. */
  180. private int requestId = 0;
  181. private int port = 0;
  182. private InetAddress address = null;
  183. private String communityString = null;
  184. String dbgTag = "SnmpInformRequest";
  185. // CONSTRUCTORS
  186. //-------------
  187. /**
  188. * For SNMP Runtime internal use only.
  189. * Constructor for creating new inform request. This object can be created only by an SNMP adaptor object.
  190. * @param session <CODE>SnmpSession</CODE> object for this inform request.
  191. * @param adp <CODE>SnmpAdaptorServer</CODE> object for this inform request.
  192. * @param addr The <CODE>InetAddress</CODE> destination for this inform request.
  193. * @param cs The community string to be used for the inform request.
  194. * @param requestCB Callback interface for the inform request.
  195. * @exception SnmpStatusException SNMP adaptor is not ONLINE or session is dead.
  196. */
  197. SnmpInformRequest(SnmpSession session,
  198. SnmpAdaptorServer adp,
  199. InetAddress addr,
  200. String cs,
  201. int p,
  202. SnmpInformHandler requestCB)
  203. throws SnmpStatusException {
  204. informSession = session;
  205. adaptor = adp;
  206. address = addr;
  207. communityString = cs;
  208. port = p;
  209. callback = requestCB;
  210. informSession.addInformRequest(this); // add to adaptor queue.
  211. setTimeout(adaptor.getTimeout()) ;
  212. }
  213. // PUBLIC METHODS
  214. //---------------
  215. /**
  216. * Gets the request id (invoke identifier) of the current inform request.
  217. * @return The request id.
  218. */
  219. final public synchronized int getRequestId () {
  220. return requestId;
  221. }
  222. /**
  223. * Gets the destination address of the current inform request.
  224. * @return The destination address.
  225. */
  226. synchronized InetAddress getAddress() {
  227. return address;
  228. }
  229. /**
  230. * Gets the current status of the inform request.
  231. * @return The current status of the inform request.
  232. */
  233. final public synchronized int getRequestStatus() {
  234. return reqState ;
  235. }
  236. /**
  237. * Indicates whether or not the inform request was aborted.
  238. * @return <CODE>true</CODE> if the inform request was aborted, <CODE>false</CODE> otherwise.
  239. */
  240. final public synchronized boolean isAborted() {
  241. return ((reqState & stAborted) == stAborted);
  242. }
  243. /**
  244. * Indicates whether or not the inform request is in progress.
  245. * @return <CODE>true</CODE> if the inform request is in progress, <CODE>false</CODE> otherwise.
  246. */
  247. final public synchronized boolean inProgress() {
  248. return ((reqState & stInProgress) == stInProgress);
  249. }
  250. /**
  251. * Indicates whether or not the inform request result is available.
  252. * @return <CODE>true</CODE> if the inform request result is available, <CODE>false</CODE> otherwise.
  253. */
  254. final public synchronized boolean isResultAvailable() {
  255. return (reqState == stResultsAvailable);
  256. }
  257. /**
  258. * Gets the status associated with the <CODE>SnmpVarBindList</CODE>.
  259. * @return The error status.
  260. */
  261. final public synchronized int getErrorStatus() {
  262. return errorStatus;
  263. }
  264. /**
  265. * Gets the index.
  266. * <P>NOTE: this value is equal to the <CODE>errorIndex</CODE> field minus 1.
  267. * @return The error index.
  268. */
  269. final public synchronized int getErrorIndex() {
  270. return errorIndex;
  271. }
  272. /**
  273. * Gets the maximum number of tries before declaring that the manager is not responding.
  274. * @return The maximum number of times an inform request should be tried.
  275. */
  276. final public int getMaxTries() {
  277. return adaptor.getMaxTries();
  278. }
  279. /**
  280. * Gets the number of tries performed for the current inform request.
  281. * @return The number of tries performed.
  282. */
  283. final public synchronized int getNumTries() {
  284. return numTries ;
  285. }
  286. /**
  287. * For SNMP Runtime internal use only.
  288. */
  289. final synchronized void setTimeout(int value) {
  290. timeout = value ;
  291. }
  292. /**
  293. * Gets absolute time in milliseconds (based on epoch time) when the next
  294. * polling activity will begin.
  295. * @return The absolute time when polling will begin.
  296. */
  297. final public synchronized long getAbsNextPollTime () {
  298. return nextPollTime ;
  299. }
  300. /**
  301. * Gets absolute time in milliseconds (based on epoch time) before which an inform
  302. * response is expected from a manager.
  303. * @return The absolute time within which an inform response is expected.
  304. */
  305. final public synchronized long getAbsMaxTimeToWait() {
  306. if (prevPollTime == 0) {
  307. return System.currentTimeMillis() ; // should never happen.
  308. } else {
  309. return waitTimeForResponse ;
  310. }
  311. }
  312. /**
  313. * Gets the <CODE>SnmpVarBindList</CODE> of the inform response.
  314. * It returns a null value if the inform request is in progress.
  315. * This ensures accidental manipulation does not occur when a request is in progress.
  316. * In case of an error, <CODE>SnmpVarBindList</CODE> is the copy
  317. * of the original <CODE>SnmpVarBindList</CODE> at the time of making the inform request.
  318. * @return The list of <CODE>SnmpVarBind</CODE> objects returned by the manager or the null value if the request
  319. * is in progress.
  320. */
  321. public final synchronized SnmpVarBindList getResponseVarBindList() {
  322. if (inProgress())
  323. return null;
  324. return varBindList;
  325. }
  326. /**
  327. * Used in synchronous mode only.
  328. * Provides a hook that enables a synchronous operation on a previously sent inform request.
  329. * Only one inform request can be in synchronous mode on a given thread.
  330. * The blocked thread is notified when the inform request state reaches completion.
  331. * If the inform request is not active, the method returns immediately.
  332. * The user must get the error status of the inform request to determine the
  333. * exact status of the request.
  334. *
  335. * @param time The amount of time to wait. Zero means block until complete.
  336. * @return <CODE>true</CODE> if the inform request has completed, <CODE>false</CODE> if it is still active.
  337. */
  338. final public boolean waitForCompletion(long time) {
  339. if (! inProgress()) // check if request is in progress.
  340. return true;
  341. if (informSession.thisSessionContext()) {
  342. // We can manipulate callback safely as we are in session thread.
  343. //
  344. SnmpInformHandler savedCallback = callback;
  345. callback = null;
  346. informSession.waitForResponse(this, time);
  347. callback = savedCallback;
  348. } else {
  349. // This is being done from a different thread. So notifyClient will do the notification.
  350. //
  351. synchronized (this) {
  352. SnmpInformHandler savedCallback = callback ;
  353. try {
  354. callback = null ;
  355. this.wait(time) ;
  356. } catch (InterruptedException e) {
  357. }
  358. callback = savedCallback ;
  359. }
  360. }
  361. return (! inProgress()); // true if request completed.
  362. }
  363. /**
  364. * Cancels the active inform request and removes itself from the polling list.
  365. */
  366. final public void cancelRequest() {
  367. errorStatus = snmpReqAborted;
  368. stopRequest();
  369. deleteRequest();
  370. notifyClient();
  371. }
  372. /**
  373. * Notifies the registered client about the completion of an operation.
  374. */
  375. final public synchronized void notifyClient() {
  376. this.notifyAll();
  377. }
  378. /**
  379. * Finalizer of the <CODE>SnmpInformRequest</CODE> objects.
  380. * This method is called by the garbage collector on an object
  381. * when garbage collection determines that there are no more references to the object.
  382. * <P>Sets all the references to this SNMP inform request object to <CODE>null</CODE>.
  383. */
  384. public void finalize() {
  385. callback = null;
  386. varBindList = null;
  387. internalVarBind = null;
  388. adaptor = null;
  389. informSession = null;
  390. requestPdu = null;
  391. responsePdu = null;
  392. }
  393. /**
  394. * Returns the <CODE>String</CODE> representation of an error code.
  395. * @param errcode The error code as an integer.
  396. * @return The error code as a <CODE>String</CODE>.
  397. */
  398. public static String snmpErrorToString(int errcode) {
  399. switch (errcode) {
  400. case snmpRspNoError :
  401. return "noError" ;
  402. case snmpRspTooBig :
  403. return "tooBig" ;
  404. case snmpRspNoSuchName :
  405. return "noSuchName" ;
  406. case snmpRspBadValue :
  407. return "badValue" ;
  408. case snmpRspReadOnly :
  409. return "readOnly" ;
  410. case snmpRspGenErr :
  411. return "genErr" ;
  412. case snmpRspNoAccess :
  413. return "noAccess" ;
  414. case snmpRspWrongType :
  415. return "wrongType" ;
  416. case snmpRspWrongLength :
  417. return "wrongLength" ;
  418. case snmpRspWrongEncoding :
  419. return "wrongEncoding" ;
  420. case snmpRspWrongValue :
  421. return "wrongValue" ;
  422. case snmpRspNoCreation :
  423. return "noCreation" ;
  424. case snmpRspInconsistentValue :
  425. return "inconsistentValue" ;
  426. case snmpRspResourceUnavailable :
  427. return "resourceUnavailable" ;
  428. case snmpRspCommitFailed :
  429. return "commitFailed" ;
  430. case snmpRspUndoFailed :
  431. return "undoFailed" ;
  432. case snmpRspAuthorizationError :
  433. return "authorizationError" ;
  434. case snmpRspNotWritable :
  435. return "notWritable" ;
  436. case snmpRspInconsistentName :
  437. return "inconsistentName" ;
  438. case snmpReqTimeout :
  439. return "reqTimeout" ;
  440. case snmpReqAborted :
  441. return "reqAborted" ;
  442. case snmpRspDecodingError :
  443. return "rspDecodingError" ;
  444. case snmpReqEncodingError :
  445. return "reqEncodingError" ;
  446. case snmpReqPacketOverflow :
  447. return "reqPacketOverflow" ;
  448. case snmpRspEndOfTable :
  449. return "rspEndOfTable" ;
  450. case snmpReqRefireAfterVbFix :
  451. return "reqRefireAfterVbFix" ;
  452. case snmpReqHandleTooBig :
  453. return "reqHandleTooBig" ;
  454. case snmpReqTooBigImpossible :
  455. return "reqTooBigImpossible" ;
  456. case snmpReqInternalError :
  457. return "reqInternalError" ;
  458. case snmpReqSocketIOError :
  459. return "reqSocketIOError" ;
  460. case snmpReqUnknownError :
  461. return "reqUnknownError" ;
  462. case snmpWrongSnmpVersion :
  463. return "wrongSnmpVersion" ;
  464. case snmpUnknownPrincipal:
  465. return "snmpUnknownPrincipal";
  466. case snmpAuthNotSupported:
  467. return "snmpAuthNotSupported";
  468. case snmpPrivNotSupported:
  469. return "snmpPrivNotSupported";
  470. case snmpBadSecurityLevel:
  471. return "snmpBadSecurityLevel";
  472. case snmpUsmBadEngineId:
  473. return "snmpUsmBadEngineId";
  474. case snmpUsmInvalidTimeliness:
  475. return "snmpUsmInvalidTimeliness";
  476. }
  477. return "Unknown Error = " + errcode;
  478. }
  479. // PRIVATE AND PACKAGE METHODS
  480. //----------------------------
  481. /**
  482. * For SNMP Runtime internal use only.
  483. * Starts an inform request in asynchronous mode. The callback interface
  484. * is used to notify the user upon request completion.
  485. * @param vblst The list of <CODE>SnmpVarBind</CODE> to be used.
  486. * @exception SnmpStatusException This inform request is already in progress.
  487. */
  488. synchronized void start(SnmpVarBindList vblst) throws SnmpStatusException {
  489. if (inProgress())
  490. throw new SnmpStatusException("Inform request already in progress.");
  491. setVarBindList(vblst);
  492. initializeAndFire();
  493. }
  494. private synchronized void initializeAndFire() {
  495. requestPdu = null;
  496. responsePdu = null;
  497. reason = null;
  498. startRequest(System.currentTimeMillis());
  499. setErrorStatusAndIndex(0, 0);
  500. }
  501. /**
  502. * This method submits the inform request for polling and marks the request
  503. * active. It does nothing if the request is already active.
  504. * The poll will be scheduled to happen immediately.
  505. * @param starttime The start time for polling.
  506. */
  507. private synchronized void startRequest(long starttime) {
  508. nextPollTime = starttime;
  509. prevPollTime = 0;
  510. schedulePoll();
  511. }
  512. /**
  513. * This method creates a new request ID. The ID is submitted to the poll server for scheduling.
  514. */
  515. private void schedulePoll() {
  516. numTries = 0;
  517. initNewRequest();
  518. setRequestStatus(stWaitingToSend);
  519. informSession.getSnmpQManager().addRequest(this);
  520. }
  521. /**
  522. * This method determines whether the inform request is to be retried. This is used if the
  523. * peer did not respond to a previous request. If the request exceeds
  524. * the maxTries limit, a timeout is signaled.
  525. */
  526. void action() {
  527. if (inProgress() == false)
  528. return;
  529. while (true) {
  530. try {
  531. if (numTries == 0) {
  532. invokeOnReady();
  533. } else if (numTries < getMaxTries()) {
  534. invokeOnRetry();
  535. } else {
  536. invokeOnTimeout();
  537. }
  538. return ;
  539. } catch (OutOfMemoryError omerr) {
  540. // Consider it as a try !
  541. //
  542. numTries++;
  543. if (isDebugOn()) {
  544. debug("action", "Inform request hit out of memory situation...");
  545. }
  546. Thread.currentThread().yield();
  547. }
  548. }
  549. }
  550. final private void invokeOnReady() {
  551. if (requestPdu == null) {
  552. requestPdu = constructPduPacket();
  553. }
  554. if (requestPdu != null) {
  555. if (sendPdu() == false)
  556. queueResponse();
  557. }
  558. }
  559. final private void invokeOnRetry() {
  560. invokeOnReady();
  561. }
  562. final private void invokeOnTimeout() {
  563. errorStatus = snmpReqTimeout;
  564. queueResponse();
  565. }
  566. final private void queueResponse() {
  567. informSession.addResponse(this);
  568. }
  569. /**
  570. * Constructs an inform request PDU.
  571. */
  572. synchronized SnmpPdu constructPduPacket() {
  573. SnmpPduPacket reqpdu = null;
  574. Exception excep = null;
  575. try {
  576. reqpdu = new SnmpPduRequest();
  577. reqpdu.port = port;
  578. reqpdu.type = pduInformRequestPdu;
  579. reqpdu.version = snmpVersionTwo;
  580. reqpdu.community = communityString.getBytes("8859_1");
  581. reqpdu.requestId = getRequestId();
  582. reqpdu.varBindList = internalVarBind;
  583. if (isTraceOn()) {
  584. trace("constructPduPacket", "Packet built");
  585. }
  586. } catch (Exception e) {
  587. excep = e;
  588. errorStatus = snmpReqUnknownError;
  589. reason = e.getMessage();
  590. }
  591. if (excep != null) {
  592. if (isDebugOn()) {
  593. debug("constructPduPacket", excep);
  594. }
  595. reqpdu = null;
  596. queueResponse();
  597. }
  598. return reqpdu;
  599. }
  600. boolean sendPdu() {
  601. try {
  602. responsePdu = null;
  603. SnmpPduFactory pduFactory = adaptor.getPduFactory();
  604. SnmpMessage msg = (SnmpMessage)pduFactory.encodeSnmpPdu((SnmpPduPacket)requestPdu, adaptor.getBufferSize().intValue());
  605. if (msg == null) {
  606. if (isDebugOn()) {
  607. debug("sendPdu", "pdu factory returned a null value");
  608. }
  609. throw new SnmpStatusException(snmpReqUnknownError);
  610. // This exception will caught hereafter and reported as an snmpReqUnknownError
  611. // FIXME: may be it's not the best behaviour ?
  612. }
  613. int maxPktSize = adaptor.getBufferSize().intValue();
  614. byte[] encoding = new byte[maxPktSize];
  615. int encodingLength = msg.encodeMessage(encoding);
  616. if (isTraceOn()) {
  617. trace("sendPdu", "Dump : \n" + msg.printMessage());
  618. }
  619. sendPduPacket(encoding, encodingLength);
  620. return true;
  621. } catch (SnmpTooBigException ar) {
  622. if (isDebugOn()) {
  623. debug("sendPdu", ar);
  624. }
  625. setErrorStatusAndIndex(snmpReqPacketOverflow, ar.getVarBindCount());
  626. requestPdu = null;
  627. reason = ar.getMessage();
  628. if (isDebugOn()) {
  629. debug("sendPdu", "Packet Overflow while building inform request");
  630. }
  631. } catch (java.io.IOException ioe) {
  632. setErrorStatusAndIndex(snmpReqSocketIOError, 0);
  633. reason = ioe.getMessage();
  634. } catch (Exception e) {
  635. if (isDebugOn()) {
  636. debug("sendPdu", e);
  637. }
  638. setErrorStatusAndIndex(snmpReqUnknownError, 0);
  639. reason = e.getMessage();
  640. }
  641. return false;
  642. }
  643. /**
  644. * Sends the prepared PDU packet to the manager and updates the data structure
  645. * to expect a response. It acquires a lock on the socket to prevent a case
  646. * where a response arrives before this thread could insert the
  647. * request into the wait queue.
  648. * @exception IOException Signals that an I/O exception of some sort has occurred.
  649. */
  650. final void sendPduPacket(byte[] buffer, int length) throws java.io.IOException {
  651. if (isTraceOn()) {
  652. trace("sendPduPacket", "Send to peer. Peer/Port : " + address.getHostName() + "/" + port +
  653. ". Length = " + length + "\nDump : \n" + SnmpMessage.dumpHexBuffer(buffer,0, length));
  654. }
  655. SnmpSocket theSocket = informSession.getSocket();
  656. synchronized (theSocket) {
  657. theSocket.sendPacket(buffer, length, address, port);
  658. setRequestSentTime(System.currentTimeMillis());
  659. }
  660. }
  661. /**
  662. * For SNMP Runtime internal use only.
  663. */
  664. final void processResponse() {
  665. if (isTraceOn()) {
  666. trace("processResponse", "errstatus = " + errorStatus);
  667. }
  668. if (inProgress() == false) { // check if this request is still alive.
  669. responsePdu = null;
  670. return; // the request may have cancelled.
  671. }
  672. if (errorStatus >= snmpReqInternalError) {
  673. handleInternalError("Internal Error...");
  674. return;
  675. }
  676. try {
  677. parsePduPacket(responsePdu);
  678. //responsePdu = null;
  679. // At this point the errorIndex is rationalized to start with 0.
  680. switch (errorStatus) {
  681. case snmpRspNoError :
  682. handleSuccess();
  683. return;
  684. case snmpReqTimeout :
  685. handleTimeout();
  686. return;
  687. case snmpReqInternalError :
  688. handleInternalError("Unknown internal error. deal with it later!");
  689. return;
  690. case snmpReqHandleTooBig :
  691. setErrorStatusAndIndex(snmpRspTooBig, 0);
  692. handleError("Cannot handle too-big situation...");
  693. return;
  694. case snmpReqRefireAfterVbFix :
  695. // Refire request after fixing varbindlist.
  696. initializeAndFire();
  697. return;
  698. default :
  699. handleError("Error status set in packet...!!");
  700. return;
  701. }
  702. } catch (Exception e) {
  703. if (isDebugOn()) {
  704. debug("processResponse", e);
  705. }
  706. reason = e.getMessage();
  707. }
  708. handleInternalError(reason);
  709. }
  710. /**
  711. * Parses the inform response packet. If the agent responds with error set,
  712. * it does not parse any further.
  713. */
  714. synchronized void parsePduPacket(SnmpPduRequestType rpdu) {
  715. if (rpdu == null)
  716. return;
  717. errorStatus = rpdu.getErrorStatus();
  718. errorIndex = rpdu.getErrorIndex();
  719. if (errorStatus == snmpRspNoError) {
  720. updateInternalVarBindWithResult(((SnmpPdu)rpdu).varBindList);
  721. return;
  722. }
  723. if (errorStatus != snmpRspNoError)
  724. --errorIndex; // rationalize for index to start with 0.
  725. if (isTraceOn()) {
  726. trace("parsePduPacket", "received inform response. ErrorStatus/ErrorIndex = " + errorStatus + "/" + errorIndex);
  727. }
  728. }
  729. /**
  730. * Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
  731. */
  732. private void handleSuccess() {
  733. setRequestStatus(stResultsAvailable);
  734. if (isTraceOn()) {
  735. trace("handleSuccess", "Invoking user defined callback...");
  736. }
  737. deleteRequest(); // delete only non-poll request.
  738. notifyClient();
  739. requestPdu = null;
  740. //responsePdu = null;
  741. internalVarBind = null;
  742. try { // catch all user exception which may happen in callback.
  743. if (callback != null)
  744. callback.processSnmpPollData(this, errorStatus, errorIndex, getVarBindList());
  745. } catch (Exception e) {
  746. if (isDebugOn()) {
  747. debug("handleSuccess", "Exception generated by user callback");
  748. debug("handleSuccess", e);
  749. }
  750. } catch (OutOfMemoryError ome) {
  751. if (isDebugOn()) {
  752. debug("handleSuccess", "OutOfMemory Error generated by user callback");
  753. debug("handleSuccess", ome);
  754. }
  755. Thread.currentThread().yield();
  756. }
  757. return;
  758. }
  759. /**
  760. * Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
  761. */
  762. private void handleTimeout() {
  763. setRequestStatus(stTimeout);
  764. if (isDebugOn()) {
  765. debug("handleTimeout", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
  766. errorIndex + ". Invoking timeout user defined callback...");
  767. }
  768. deleteRequest();
  769. notifyClient();
  770. requestPdu = null;
  771. responsePdu = null;
  772. internalVarBind = null;
  773. try {
  774. if (callback != null)
  775. callback.processSnmpPollTimeout(this);
  776. } catch (Exception e) { // catch any exception a user might not handle.
  777. if (isDebugOn()) {
  778. debug("handleTimeout", "Exception generated by user callback");
  779. debug("handleTimeout", e);
  780. }
  781. } catch (OutOfMemoryError ome) {
  782. if (isDebugOn()) {
  783. debug("handleTimeout", "OutOfMemory Error generated by user callback");
  784. debug("handleTimeout", ome);
  785. }
  786. Thread.currentThread().yield();
  787. }
  788. return;
  789. }
  790. /**
  791. * Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
  792. */
  793. private void handleError(String msg) {
  794. setRequestStatus(stResultsAvailable);
  795. if (isDebugOn()) {
  796. debug("handleError", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
  797. errorIndex + ". Invoking error user defined callback...\n" + getVarBindList());
  798. }
  799. deleteRequest();
  800. notifyClient();
  801. requestPdu = null;
  802. responsePdu = null;
  803. internalVarBind = null;
  804. try {
  805. if (callback != null)
  806. callback.processSnmpPollData(this, getErrorStatus(), getErrorIndex(), getVarBindList());
  807. } catch (Exception e) { // catch any exception a user might not handle.
  808. if (isDebugOn()) {
  809. debug("handleError", "Exception generated by user callback");
  810. debug("handleError", e);
  811. }
  812. } catch (OutOfMemoryError ome) {
  813. if (isDebugOn()) {
  814. debug("handleError", "OutOfMemory Error generated by user callback");
  815. debug("handleError", ome);
  816. }
  817. Thread.currentThread().yield();
  818. }
  819. }
  820. /**
  821. * Calls the user implementation of the <CODE>SnmpInformHandler</CODE> interface.
  822. */
  823. private void handleInternalError(String msg) {
  824. setRequestStatus(stInternalError);
  825. if (reason == null)
  826. reason = msg;
  827. if (isDebugOn()) {
  828. debug("handleInternalError", "Snmp error/index = " + snmpErrorToString(errorStatus) + "/" +
  829. errorIndex + ". Invoking internal error user defined callback...\n" + getVarBindList());
  830. }
  831. deleteRequest();
  832. notifyClient();
  833. requestPdu = null;
  834. responsePdu = null;
  835. internalVarBind = null;
  836. try {
  837. if (callback != null)
  838. callback.processSnmpInternalError(this, reason);
  839. } catch (Exception e) { // catch any exception a user might not handle.
  840. if (isDebugOn()) {
  841. debug("handleInternalError", "Exception generated by user callback");
  842. debug("handleInternalError", e);
  843. }
  844. } catch (OutOfMemoryError ome) {
  845. if (isDebugOn()) {
  846. debug("handleInternalError", "OutOfMemory Error generated by user callback");
  847. debug("handleInternalError", ome);
  848. }
  849. Thread.currentThread().yield();
  850. }
  851. }
  852. void updateInternalVarBindWithResult(SnmpVarBind[] list) {
  853. if ((list == null) || (list.length == 0))
  854. return;
  855. int idx = 0;
  856. for(int i = 0; i < internalVarBind.length && idx < list.length; i++) {
  857. SnmpVarBind avar = internalVarBind[i];
  858. if (avar == null)
  859. continue;
  860. SnmpVarBind res = list[idx];
  861. avar.setSnmpValue(res.getSnmpValue());
  862. idx++;
  863. }
  864. }
  865. /**
  866. * For SNMP Runtime internal use only.
  867. */
  868. final void invokeOnResponse(Object resp) {
  869. if (resp != null) {
  870. if (resp instanceof SnmpPduRequestType)
  871. responsePdu = (SnmpPduRequestType) resp;
  872. else
  873. return;
  874. }
  875. setRequestStatus(stReceivedReply);
  876. queueResponse();
  877. }
  878. /**
  879. * This method cancels an active inform request and removes it from the polling list.
  880. */
  881. private void stopRequest() {
  882. // Remove the clause synchronized of the stopRequest method.
  883. // Synchronization is isolated as possible to avoid thread lock.
  884. // Note: the method removeRequest from SendQ is synchronized.
  885. // fix bug jaw.00392.B
  886. //
  887. synchronized(this) {
  888. setRequestStatus(stAborted);
  889. }
  890. informSession.getSnmpQManager().removeRequest(this);
  891. synchronized(this) {
  892. requestId = 0;
  893. }
  894. }
  895. final synchronized void deleteRequest() {
  896. informSession.removeInformRequest(this);
  897. }
  898. /**
  899. * For SNMP Runtime internal use only.
  900. * Gets the active <CODE>SnmpVarBindList</CODE>. The contents of it
  901. * are not guaranteed to be consistent when the inform request is active.
  902. * @return The list of <CODE>SnmpVarBind</CODE> objects.
  903. */
  904. final synchronized SnmpVarBindList getVarBindList() {
  905. return varBindList;
  906. }
  907. /**
  908. * For SNMP Runtime internal use only.
  909. * You should specify the <CODE>SnmpVarBindList</CODE> at SnmpInformRequest creation time.
  910. * You cannot modify it during the life-time of the object.
  911. */
  912. final synchronized void setVarBindList(SnmpVarBindList newvblst) {
  913. varBindList = newvblst;
  914. if (internalVarBind == null || internalVarBind.length != varBindList.size()) {
  915. internalVarBind = new SnmpVarBind[varBindList.size()];
  916. }
  917. varBindList.copyInto(internalVarBind);
  918. }
  919. /**
  920. * For SNMP Runtime internal use only.
  921. */
  922. final synchronized void setErrorStatusAndIndex(int stat, int idx) {
  923. errorStatus = stat;
  924. errorIndex = idx;
  925. }
  926. /**
  927. * For SNMP Runtime internal use only.
  928. */
  929. final synchronized void setPrevPollTime(long prev) {
  930. prevPollTime = prev;
  931. }
  932. /**
  933. * For SNMP Runtime internal use only.
  934. */
  935. final void setRequestSentTime(long sendtime) {
  936. numTries++;
  937. setPrevPollTime(sendtime);
  938. waitTimeForResponse = prevPollTime + timeout*numTries;
  939. setRequestStatus(stWaitingForReply);
  940. if (isTraceOn()) {
  941. trace("setRequestSentTime", "Inform request Successfully sent");
  942. }
  943. informSession.getSnmpQManager().addWaiting(this);
  944. }
  945. /**
  946. * Initializes the request id from the request counter.
  947. */
  948. final synchronized void initNewRequest() {
  949. requestId = requestCounter.getNewId();
  950. }
  951. /**
  952. * For SNMP Runtime internal use only.
  953. */
  954. long timeRemainingForAction(long currtime) {
  955. switch (reqState) {
  956. case stWaitingToSend :
  957. return nextPollTime - currtime;
  958. case stWaitingForReply :
  959. return waitTimeForResponse - currtime;
  960. default :
  961. return -1;
  962. }
  963. }
  964. /**
  965. * Returns the string state corresponding to the specified integer state.
  966. * @param state The integer state.
  967. * @return The string state.
  968. */
  969. final static String statusDescription(int state) {
  970. switch (state) {
  971. case stWaitingToSend :
  972. return "Waiting to send.";
  973. case stWaitingForReply :
  974. return "Waiting for reply.";
  975. case stReceivedReply :
  976. return "Response arrived.";
  977. case stAborted :
  978. return "Aborted by user.";
  979. case stTimeout :
  980. return "Timeout Occured.";
  981. case stInternalError :
  982. return "Internal error.";
  983. case stResultsAvailable :
  984. return "Results available";
  985. case stNeverUsed :
  986. return "Inform request in createAndWait state";
  987. }
  988. return "Unknown inform request state.";
  989. }
  990. /**
  991. * Sets the request status to the specified value.
  992. * @param reqst The new status request.
  993. */
  994. final synchronized void setRequestStatus(int reqst) {
  995. reqState = reqst;
  996. }
  997. /**
  998. * Gives a status report of the request.
  999. * @return The status report of the request.
  1000. */
  1001. public synchronized String toString() {
  1002. StringBuffer s = new StringBuffer(300) ;
  1003. s.append(tostring()) ;
  1004. s.append("\nPeer/Port : " + address.getHostName() + "/" + port) ;
  1005. return s.toString() ;
  1006. }
  1007. private synchronized String tostring() {
  1008. StringBuffer s = new StringBuffer("InformRequestId = " + requestId);
  1009. s.append(" " + "Status = " + statusDescription(reqState));
  1010. s.append(" Timeout/MaxTries/NumTries = " + timeout*numTries + "/" +
  1011. + getMaxTries() + "/" + numTries);
  1012. if (prevPollTime > 0) {
  1013. debugDate.setTime(prevPollTime);
  1014. s.append("\nPrevPolled = " + debugDate.toString());
  1015. } else
  1016. s.append("\nNeverPolled");
  1017. s.append(" / RemainingTime(millis) = " +
  1018. timeRemainingForAction(System.currentTimeMillis()));
  1019. return s.toString();
  1020. }
  1021. // TRACES & DEBUG
  1022. //---------------
  1023. boolean isTraceOn() {
  1024. return Trace.isSelected(Trace.LEVEL_TRACE, Trace.INFO_ADAPTOR_SNMP);
  1025. }
  1026. void trace(String clz, String func, String info) {
  1027. Trace.send(Trace.LEVEL_TRACE, Trace.INFO_ADAPTOR_SNMP, clz, func, info);
  1028. }
  1029. void trace(String func, String info) {
  1030. trace(dbgTag, func, info);
  1031. }
  1032. boolean isDebugOn() {
  1033. return Trace.isSelected(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP);
  1034. }
  1035. void debug(String clz, String func, String info) {
  1036. Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP, clz, func, info);
  1037. }
  1038. void debug(String clz, String func, Throwable exception) {
  1039. Trace.send(Trace.LEVEL_DEBUG, Trace.INFO_ADAPTOR_SNMP, clz, func, exception);
  1040. }
  1041. void debug(String func, String info) {
  1042. debug(dbgTag, func, info);
  1043. }
  1044. void debug(String func, Throwable exception) {
  1045. debug(dbgTag, func, exception);
  1046. }
  1047. }