KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > snmp4j > Snmp


1 /*_############################################################################
2   _##
3   _## SNMP4J - Snmp.java
4   _##
5   _## Copyright 2003-2007 Frank Fock and Jochen Katz (SNMP4J.org)
6   _##
7   _## Licensed under the Apache License, Version 2.0 (the "License");
8   _## you may not use this file except in compliance with the License.
9   _## You may obtain a copy of the License at
10   _##
11   _## http://www.apache.org/licenses/LICENSE-2.0
12   _##
13   _## Unless required by applicable law or agreed to in writing, software
14   _## distributed under the License is distributed on an "AS IS" BASIS,
15   _## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   _## See the License for the specific language governing permissions and
17   _## limitations under the License.
18   _##
19   _##########################################################################*/

20
21 package org.snmp4j;
22
23 import java.io.IOException JavaDoc;
24 import java.util.*;
25
26 import org.snmp4j.event.*;
27 import org.snmp4j.log.*;
28 import org.snmp4j.mp.*;
29 import org.snmp4j.security.*;
30 import org.snmp4j.smi.*;
31 import org.snmp4j.transport.TransportMappings;
32 import org.snmp4j.transport.ConnectionOrientedTransportMapping;
33
34 /**
35  * The <code>Snmp</code> class is the core of SNMP4J. It provides functions to
36  * send and receive SNMP PDUs. All SNMP PDU types can be send. Confirmed
37  * PDUs can be sent synchronously and asynchronously.
38  * <p>
39  * The <code>Snmp</code> class is transport protocol independent. Support for
40  * a specific {@link TransportMapping} instance is added by calling the
41  * {@link #addTransportMapping(TransportMapping transportMapping)} method or
42  * creating a <code>Snmp</code> instance by using the non-default constructor
43  * with the corresponding transport mapping. Transport mappings are used
44  * for incoming and outgoing messages.
45  * <p>
46  * To setup a default SNMP session for UDP transport and with SNMPv3 support
47  * the following code snippet can be used:
48  * <p>
49  * <pre>
50  * Address targetAddress = GenericAddress.parse("udp:127.0.0.1/161");
51  * TransportMapping transport = new DefaultUdpTransportMapping();
52  * snmp = new Snmp(transport);
53  * USM usm = new USM(SecurityProtocols.getInstance(),
54  * new OctetString(MPv3.createLocalEngineID()), 0);
55  * SecurityModels.getInstance().addSecurityModel(usm);
56  * transport.listen();
57  * </pre>
58  * <p>
59  * How a synchronous SNMPv3 message with authentication and privacy is then
60  * sent illustrates the following code snippet:
61  * <p>
62  * <pre>
63  * // add user to the USM
64  * snmp.getUSM().addUser(new OctetString("MD5DES"),
65  * new UsmUser(new OctetString("MD5DES"),
66  * AuthMD5.ID,
67  * new OctetString("MD5DESUserAuthPassword"),
68  * PrivDES.ID,
69  * new OctetString("MD5DESUserPrivPassword")));
70  * // create the target
71  * UserTarget target = new UserTarget();
72  * target.setAddress(targetAddress);
73  * target.setRetries(1);
74  * target.setTimeout(5000);
75  * target.setVersion(SnmpConstants.version3);
76  * target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
77  * target.setSecurityName(new OctetString("MD5DES"));
78  *
79  * // create the PDU
80  * PDU pdu = new ScopedPDU();
81  * pdu.add(new VariableBinding(new OID("1.3.6")));
82  * pdu.setType(PDU.GETNEXT);
83  *
84  * // send the PDU
85  * ResponseEvent response = snmp.send(pdu, target);
86  * // extract the response PDU (could be null if timed out)
87  * PDU responsePDU = response.getResponse();
88  * // extract the address used by the agent to send the response:
89  * Address peerAddress = response.getPeerAddress();
90  * </pre>
91  * <p>
92  * An asynchronous SNMPv1 request is sent by the following code:
93  * <pre>
94  * // setting up target
95  * CommunityTarget target = new CommunityTarget();
96  * target.setCommunity(new OctetString("public"));
97  * target.setAddress(targetAddress);
98  * target.setRetries(2);
99  * target.setTimeout(1500);
100  * target.setVersion(SnmpConstants.version1);
101  * // creating PDU
102  * PDU pdu = new PDU();
103  * pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,1})));
104  * pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2})));
105  * pdu.setType(PDU.GETNEXT);
106  * // sending request
107  * ResponseListener listener = new ResponseListener() {
108  * public void onResponse(ResponseEvent event) {
109  * // Always cancel async request when response has been received
110  * // otherwise a memory leak is created! Not canceling a request
111  * // immediately can be useful when sending a request to a broadcast
112  * // address.
113  * ((Snmp)event.getSource()).cancel(event.getRequest(), this);
114  * System.out.println("Received response PDU is: "+event.getResponse());
115  * }
116  * };
117  * snmp.sendPDU(pdu, target, null, listener);
118  * </pre>
119  * </p>
120  * Traps (notifications) and other SNMP PDUs can be received by adding the
121  * folling code to the first code snippet above:
122  * <pre>
123  * CommandResponder trapPrinter = new CommandResponder() {
124  * public synchronized void processPdu(CommandResponderEvent e) {
125  * PDU command = e.getPdu();
126  * if (command != null) {
127  * System.out.println(command.toString());
128  * }
129  * }
130  * };
131  * snmp.addCommandResponder(trapPrinter);
132  * </pre>
133  * </p>
134  *
135  * @author Frank Fock
136  * @version 1.8
137  */

138 public class Snmp implements Session, CommandResponder {
139
140   private static final LogAdapter logger = LogFactory.getLogger(Snmp.class);
141
142   // Message processing implementation
143
private MessageDispatcher messageDispatcher;
144
145   /**
146    * The <code>pendingRequests</code> table contains pending requests
147    * accessed trough the key <code>PduHandle</code>
148    */

149   private Hashtable pendingRequests = new Hashtable(50);
150
151   /**
152    * The <code>asyncRequests</code> table contains pending requests
153    * accessed trough the key userObject
154    */

155   private Hashtable asyncRequests = new Hashtable(50);
156
157   // Timer for retrying pending requests
158
private Timer timer = new Timer(true);
159
160   // Listeners for request and trap PDUs
161
private transient Vector commandResponderListeners;
162
163   private TimeoutModel timeoutModel = new DefaultTimeoutModel();
164
165   // Dispatcher for notification listeners - not needed by default
166
private NotificationDispatcher notificationDispatcher = null;
167
168   // Default ReportHandler
169
private ReportHandler reportHandler = new ReportProcessor();
170
171   /**
172    * Creates a <code>Snmp</code> instance that uses a
173    * <code>MessageDispatcherImpl</code> with no message processing
174    * models and no security protols (by default). You will have to add
175    * those by calling the appropriate methods on
176    * {@link #getMessageDispatcher()}.
177    * <p>
178    * At least one transport mapping has to be added before {@link #listen()}
179    * is called in order to be able to send and receive SNMP messages.
180    */

181   public Snmp() {
182     this.messageDispatcher = new MessageDispatcherImpl();
183   }
184
185   /**
186    * Interface for handling reports.
187    *
188    * @author Frank Fock
189    * @version 1.6
190    * @since 1.6
191    */

192   public static interface ReportHandler {
193     void processReport(PduHandle pduHandle, CommandResponderEvent event);
194   }
195
196   protected final void initMessageDispatcher() {
197     this.messageDispatcher.addCommandResponder(this);
198     this.messageDispatcher.addMessageProcessingModel(new MPv2c());
199     this.messageDispatcher.addMessageProcessingModel(new MPv1());
200     this.messageDispatcher.addMessageProcessingModel(new MPv3());
201     SecurityProtocols.getInstance().addDefaultProtocols();
202   }
203
204   /**
205    * Creates a <code>Snmp</code> instance that uses a
206    * <code>MessageDispatcherImpl</code> with all supported message processing
207    * models and the default security protols for dispatching.
208    *
209    * @param transportMapping TransportMapping
210    * the initial <code>TransportMapping</code>. You can add more or remove
211    * the same later.
212    */

213   public Snmp(TransportMapping transportMapping) {
214     this();
215     initMessageDispatcher();
216     if (transportMapping != null) {
217       addTransportMapping(transportMapping);
218     }
219   }
220
221   /**
222    * Creates a <code>Snmp</code> instance by supplying a <code>
223    * MessageDispatcher</code> and a <code>TransportMapping</code>.
224    * <p>
225    * As of version 1.1, the supplied message dispatcher is not altered
226    * in terms of adding any message processing models to it. This has to be
227    * done now outside the Snmp class.
228    *
229    * @param messageDispatcher
230    * a <code>MessageDispatcher</code> instance that will be used to
231    * dispatch incoming and outgoing messages.
232    * @param transportMapping
233    * the initial <code>TransportMapping</code>,
234    * which may be <code>null</code>. You can add or remove transport
235    * mappings later using {@link #addTransportMapping} and
236    * {@link #removeTransportMapping} respectively.
237    */

238   public Snmp(MessageDispatcher messageDispatcher,
239               TransportMapping transportMapping) {
240     this.messageDispatcher = messageDispatcher;
241     this.messageDispatcher.addCommandResponder(this);
242     if (transportMapping != null) {
243       addTransportMapping(transportMapping);
244     }
245   }
246
247   /**
248    * Creates a <code>Snmp</code> instance by supplying a <code>
249    * MessageDispatcher</code>.
250    * <p>
251    * The supplied message dispatcher is not altered
252    * in terms of adding any message processing models to it. This has to be
253    * done now outside the Snmp class.
254    * </p>
255    * <p>
256    * Do not forget to add at least one transport mapping before calling the
257    * listen method!
258    * </p>
259    * @param messageDispatcher
260    * a <code>MessageDispatcher</code> instance that will be used to
261    * dispatch incoming and outgoing messages.
262    * @since 1.5
263    */

264   public Snmp(MessageDispatcher messageDispatcher) {
265     this.messageDispatcher = messageDispatcher;
266     this.messageDispatcher.addCommandResponder(this);
267   }
268
269   /**
270    * Returns the message dispatcher associated with this SNMP session.
271    * @return
272    * a <code>MessageDispatcher</code> instance.
273    * @since 1.1
274    */

275   public MessageDispatcher getMessageDispatcher() {
276     return messageDispatcher;
277   }
278
279   /**
280    * Adds a <code>TransportMapping</code> to this SNMP session.
281    * @param transportMapping
282    * a <code>TransportMapping</code> instance.
283    */

284   public void addTransportMapping(TransportMapping transportMapping) {
285     // connect transport mapping with message dispatcher
286
messageDispatcher.addTransportMapping(transportMapping);
287     transportMapping.addTransportListener(messageDispatcher);
288   }
289
290   /**
291    * Removes the specified transport mapping from this SNMP session.
292    * If the transport mapping is not currently part of this SNMP session,
293    * this method will have no effect.
294    * @param transportMapping
295    * a previously added <code>TransportMapping</code>.
296    */

297   public void removeTransportMapping(TransportMapping transportMapping) {
298     messageDispatcher.removeTransportMapping(transportMapping);
299     transportMapping.removeTransportListener(messageDispatcher);
300   }
301
302   /**
303    * Adds a notification listener to this Snmp instance. Calling this method
304    * will create a transport mapping for the specified listening address and
305    * registers the provided <code>CommandResponder</code> with the internal
306    * <code>NotificationDispatcher</code>.
307    *
308    * @param listenAddress
309    * the <code>Address</code> denoting the transport end-point
310    * (interface and port) to listen for incoming notifications.
311    * @param listener
312    * the <code>CommandResponder</code> instance that should handle
313    * the received notifications.
314    * @return
315    * <code>true</code> if registration was successful and <code>false</code>
316    * if, for example, the transport mapping for the listen address could not
317    * be created.
318    * @since 1.6
319    */

320   public synchronized boolean addNotificationListener(Address listenAddress,
321                                                       CommandResponder listener)
322   {
323     TransportMapping tm =
324         TransportMappings.getInstance().createTransportMapping(listenAddress);
325     if (tm == null) {
326       if (logger.isInfoEnabled()) {
327         logger.info("Failed to add notification listener for address: "+
328                     listenAddress);
329       }
330       return false;
331     }
332     if (tm instanceof ConnectionOrientedTransportMapping) {
333       ((ConnectionOrientedTransportMapping)tm).setConnectionTimeout(0);
334     }
335     tm.addTransportListener(messageDispatcher);
336     if (notificationDispatcher == null) {
337       notificationDispatcher = new NotificationDispatcher();
338       addCommandResponder(notificationDispatcher);
339     }
340     notificationDispatcher.addNotificationListener(listenAddress, tm, listener);
341     try {
342       tm.listen();
343       if (logger.isInfoEnabled()) {
344         logger.info("Added notification listener for address: "+
345                     listenAddress);
346       }
347       return true;
348     }
349     catch (IOException JavaDoc ex) {
350       logger.warn("Failed to initialize notification listener for address '"+
351                   listenAddress+"': "+ex.getMessage());
352       return false;
353     }
354   }
355
356   /**
357    * Removes (deletes) the notification listener for the specified transport
358    * endpoint.
359    * @param listenAddress
360    * the listen <code>Address</code> to be removed.
361    * @return
362    * <code>true</code> if the notification listener has been removed
363    * successfully.
364    */

365   public synchronized boolean removeNotificationListener(Address listenAddress)
366   {
367     if (notificationDispatcher != null) {
368       if (logger.isInfoEnabled()) {
369         logger.info("Removing notification listener for address: "+
370                     listenAddress);
371       }
372       return notificationDispatcher.removeNotificationListener(listenAddress);
373     }
374     else {
375       return false;
376     }
377   }
378
379   /**
380    * Puts all associated transport mappings into listen mode.
381    * @throws IOException
382    * if a transport mapping throws an <code>IOException</code> when its
383    * {@link TransportMapping#listen()} method has been called.
384    */

385   public void listen() throws IOException JavaDoc {
386     for (Iterator it = messageDispatcher.getTransportMappings().iterator();
387          it.hasNext(); ) {
388       TransportMapping tm = (TransportMapping) it.next();
389       if (!tm.isListening()) {
390         tm.listen();
391       }
392     }
393   }
394
395   /**
396    * Gets the next unique request ID. The returned ID is unique across
397    * the last 2^31-1 IDs generated by this message dispatcher.
398    * @return
399    * an integer value in the range 1..2^31-1. The returned ID can be used
400    * to map responses to requests send through this message dispatcher.
401    * @since 1.1
402    * @see MessageDispatcher#getNextRequestID
403    */

404   public int getNextRequestID() {
405     return messageDispatcher.getNextRequestID();
406   }
407
408   /**
409    * Closes the session and frees any allocated resources, i.e. sockets and
410    * the internal thread for processing request timeouts.
411    * <p>
412    * If there are any pending requests, the {@link ResponseListener} associated
413    * with the pending requests, will be called with a <code>null</code>
414    * response and a {@link InterruptedException} in the error member of the
415    * {@link ResponseEvent} returned.
416    * <p>
417    * After a <code>Session</code> has been closed it must not be used anymore.
418    * @throws IOException
419    * if a transport mapping cannot be closed successfully.
420    */

421   public void close() throws IOException JavaDoc {
422     for (Iterator it = messageDispatcher.getTransportMappings().iterator();
423          it.hasNext(); ) {
424       ((TransportMapping) it.next()).close();
425     }
426     timer.cancel();
427     for (Iterator it = pendingRequests.values().iterator(); it.hasNext(); ) {
428       PendingRequest pending = (PendingRequest) it.next();
429       ResponseEvent e =
430           new ResponseEvent(this, null, pending.pdu, null, pending.userObject,
431                             new InterruptedException JavaDoc(
432           "Snmp session has been closed"));
433       pending.listener.onResponse(e);
434     }
435     // close all notification listeners
436
if (notificationDispatcher != null) {
437       notificationDispatcher.closeAll();
438     }
439   }
440
441   /**
442    * Sends a GET request to a target. This method sets the PDU's type to
443    * {@link PDU#GET} and then sends a synchronous request to the supplied
444    * target.
445    * @param pdu
446    * a <code>PDU</code> instance. For SNMPv3 messages, the supplied PDU
447    * instance has to be a <code>ScopedPDU</code> instance.
448    * @param target
449    * the Target instance representing the target SNMP engine where to send
450    * the <code>pdu</code>.
451    * @return
452    * the received response encapsulated in a <code>ResponseEvent</code>
453    * instance. To obtain the received response <code>PDU</code> call
454    * {@link ResponseEvent#getResponse()}. If the request timed out,
455    * that method will return <code>null</code>.
456    * @throws IOException
457    * if the PDU cannot be sent to the target.
458    * @since 1.1
459    */

460   public ResponseEvent get(PDU pdu, Target target) throws IOException JavaDoc {
461     pdu.setType(PDU.GET);
462     return send(pdu, target);
463   }
464
465   /**
466    * Asynchronously sends a GET request <code>PDU</code> to the given target.
467    * The response is then returned by calling the supplied
468    * <code>ResponseListener</code> instance.
469    *
470    * @param pdu
471    * the PDU instance to send.
472    * @param target
473    * the Target instance representing the target SNMP engine where to send
474    * the <code>pdu</code>.
475    * @param userHandle
476    * an user defined handle that is returned when the request is returned
477    * via the <code>listener</code> object.
478    * @param listener
479    * a <code>ResponseListener</code> instance that is called when
480    * <code>pdu</code> is a confirmed PDU and the request is either answered
481    * or timed out.
482    * @throws IOException
483    * if the PDU cannot be sent to the target.
484    * @since 1.1
485    */

486   public void get(PDU pdu, Target target, Object JavaDoc userHandle,
487                   ResponseListener listener) throws IOException JavaDoc {
488     pdu.setType(PDU.GET);
489     send(pdu, target, userHandle, listener);
490   }
491
492   /**
493    * Sends a GETNEXT request to a target. This method sets the PDU's type to
494    * {@link PDU#GETNEXT} and then sends a synchronous request to the supplied
495    * target. This method is a convenience wrapper for the
496    * {@link #send(PDU pdu, Target target)} method.
497    * @param pdu
498    * a <code>PDU</code> instance. For SNMPv3 messages, the supplied PDU
499    * instance has to be a <code>ScopedPDU</code> instance.
500    * @param target
501    * the Target instance representing the target SNMP engine where to send
502    * the <code>pdu</code>.
503    * @return
504    * the received response encapsulated in a <code>ResponseEvent</code>
505    * instance. To obtain the received response <code>PDU</code> call
506    * {@link ResponseEvent#getResponse()}. If the request timed out,
507    * that method will return <code>null</code>.
508    * @throws IOException
509    * if the PDU cannot be sent to the target.
510    * @since 1.1
511    */

512   public ResponseEvent getNext(PDU pdu, Target target) throws IOException JavaDoc {
513     pdu.setType(PDU.GETNEXT);
514     return send(pdu, target);
515   }
516
517   /**
518    * Asynchronously sends a GETNEXT request <code>PDU</code> to the given
519    * target. The response is then returned by calling the supplied
520    * <code>ResponseListener</code> instance.
521    *
522    * @param pdu
523    * the PDU instance to send.
524    * @param target
525    * the Target instance representing the target SNMP engine where to send
526    * the <code>pdu</code>.
527    * @param userHandle
528    * an user defined handle that is returned when the request is returned
529    * via the <code>listener</code> object.
530    * @param listener
531    * a <code>ResponseListener</code> instance that is called when
532    * <code>pdu</code> is a confirmed PDU and the request is either answered
533    * or timed out.
534    * @throws IOException
535    * if the PDU cannot be sent to the target.
536    * @since 1.1
537    */

538   public void getNext(PDU pdu, Target target, Object JavaDoc userHandle,
539                       ResponseListener listener) throws IOException JavaDoc {
540     pdu.setType(PDU.GETNEXT);
541     send(pdu, target, userHandle, listener);
542   }
543
544   /**
545    * Sends a GETBULK request to a target. This method sets the PDU's type to
546    * {@link PDU#GETBULK} and then sends a synchronous request to the supplied
547    * target. This method is a convenience wrapper for the
548    * {@link #send(PDU pdu, Target target)} method.
549    * @param pdu
550    * a <code>PDU</code> instance. For SNMPv3 messages, the supplied PDU
551    * instance has to be a <code>ScopedPDU</code> instance.
552    * @param target
553    * the Target instance representing the target SNMP engine where to send
554    * the <code>pdu</code>.
555    * @return
556    * the received response encapsulated in a <code>ResponseEvent</code>
557    * instance. To obtain the received response <code>PDU</code> call
558    * {@link ResponseEvent#getResponse()}. If the request timed out,
559    * that method will return <code>null</code>.
560    * @throws IOException
561    * if the PDU cannot be sent to the target.
562    * @since 1.1
563    */

564   public ResponseEvent getBulk(PDU pdu, Target target) throws IOException JavaDoc {
565     pdu.setType(PDU.GETBULK);
566     return send(pdu, target);
567   }
568
569   /**
570    * Asynchronously sends a GETBULK request <code>PDU</code> to the given
571    * target. The response is then returned by calling the supplied
572    * <code>ResponseListener</code> instance.
573    *
574    * @param pdu
575    * the PDU instance to send.
576    * @param target
577    * the Target instance representing the target SNMP engine where to send
578    * the <code>pdu</code>.
579    * @param userHandle
580    * an user defined handle that is returned when the request is returned
581    * via the <code>listener</code> object.
582    * @param listener
583    * a <code>ResponseListener</code> instance that is called when
584    * <code>pdu</code> is a confirmed PDU and the request is either answered
585    * or timed out.
586    * @throws IOException
587    * if the PDU cannot be sent to the target.
588    * @since 1.1
589    */

590   public void getBulk(PDU pdu, Target target, Object JavaDoc userHandle,
591                       ResponseListener listener) throws IOException JavaDoc {
592     pdu.setType(PDU.GETBULK);
593     send(pdu, target, userHandle, listener);
594   }
595
596   /**
597    * Sends an INFORM request to a target. This method sets the PDU's type to
598    * {@link PDU#INFORM} and then sends a synchronous request to the supplied
599    * target. This method is a convenience wrapper for the
600    * {@link #send(PDU pdu, Target target)} method.
601    * @param pdu
602    * a <code>PDU</code> instance. For SNMPv3 messages, the supplied PDU
603    * instance has to be a <code>ScopedPDU</code> instance.
604    * @param target
605    * the Target instance representing the target SNMP engine where to send
606    * the <code>pdu</code>.
607    * @return
608    * the received response encapsulated in a <code>ResponseEvent</code>
609    * instance. To obtain the received response <code>PDU</code> call
610    * {@link ResponseEvent#getResponse()}. If the request timed out,
611    * that method will return <code>null</code>.
612    * @throws IOException
613    * if the inform request could not be send to the specified target.
614    * @since 1.1
615    */

616   public ResponseEvent inform(PDU pdu, Target target) throws IOException JavaDoc {
617     pdu.setType(PDU.INFORM);
618     return send(pdu, target);
619   }
620
621   /**
622    * Asynchronously sends an INFORM request <code>PDU</code> to the given
623    * target. The response is then returned by calling the supplied
624    * <code>ResponseListener</code> instance.
625    *
626    * @param pdu
627    * the PDU instance to send.
628    * @param target
629    * the Target instance representing the target SNMP engine where to send
630    * the <code>pdu</code>.
631    * @param userHandle
632    * an user defined handle that is returned when the request is returned
633    * via the <code>listener</code> object.
634    * @param listener
635    * a <code>ResponseListener</code> instance that is called when
636    * <code>pdu</code> is a confirmed PDU and the request is either answered
637    * or timed out.
638    * @throws IOException
639    * if the PDU cannot be sent to the target.
640    * @since 1.1
641    */

642   public void inform(PDU pdu, Target target, Object JavaDoc userHandle,
643                      ResponseListener listener) throws IOException JavaDoc {
644     pdu.setType(PDU.INFORM);
645     send(pdu, target, userHandle, listener);
646   }
647
648   /**
649    * Sends a SNMPv1 trap to a target. This method sets the PDU's type to
650    * {@link PDU#V1TRAP} and then sends it to the supplied target. This method
651    * is a convenience wrapper for the {@link #send(PDU pdu, Target target)}
652    * method.
653    * @param pdu
654    * a <code>PDUv1</code> instance.
655    * @param target
656    * the Target instance representing the target SNMP engine where to send
657    * the <code>pdu</code>. The selected SNMP protocol version for the
658    * target must be {@link SnmpConstants#version1}.
659    * @throws IOException
660    * if the trap cannot be sent.
661    * @since 1.1
662    */

663   public void trap(PDUv1 pdu, Target target) throws IOException JavaDoc {
664     if (target.getVersion() != SnmpConstants.version1) {
665       throw new IllegalArgumentException JavaDoc(
666           "SNMPv1 trap PDU must be used with SNMPv1");
667     }
668     pdu.setType(PDU.V1TRAP);
669     send(pdu, target);
670   }
671
672   /**
673    * Sends a SNMPv2c or SNMPv3 notification to a target. This method sets the
674    * PDU's type to {@link PDU#NOTIFICATION} and then sends it to the supplied
675    * target. This method is a convenience wrapper for the
676    * {@link #send(PDU pdu, Target target)} method.
677    * @param pdu
678    * a <code>PDUv1</code> instance.
679    * @param target
680    * the Target instance representing the target SNMP engine where to send
681    * the <code>pdu</code>. The selected SNMP protocol version for the
682    * target must be {@link SnmpConstants#version2c} or
683    * {@link SnmpConstants#version2c}.
684    * @throws IOException
685    * if the notification cannot be sent.
686    * @since 1.1
687    */

688   public void notify(PDU pdu, Target target) throws IOException JavaDoc {
689     if (target.getVersion() == SnmpConstants.version1) {
690       throw new IllegalArgumentException JavaDoc(
691           "Notifications PDUs cannot be used with SNMPv1");
692     }
693     pdu.setType(PDU.NOTIFICATION);
694     send(pdu, target);
695   }
696
697
698   /**
699    * Sends a SET request to a target. This method sets the PDU's type to
700    * {@link PDU#SET} and then sends a synchronous request to the supplied
701    * target.
702    * @param pdu
703    * a <code>PDU</code> instance. For SNMPv3 messages, the supplied PDU
704    * instance has to be a <code>ScopedPDU</code> instance.
705    * @param target
706    * the Target instance representing the target SNMP engine where to send
707    * the <code>pdu</code>.
708    * @return
709    * the received response encapsulated in a <code>ResponseEvent</code>
710    * instance. To obtain the received response <code>PDU</code> call
711    * {@link ResponseEvent#getResponse()}. If the request timed out,
712    * that method will return <code>null</code>.
713    * @since 1.1
714    */

715   public ResponseEvent set(PDU pdu, Target target) {
716     pdu.setType(PDU.SET);
717     try {
718       return send(pdu, target);
719     }
720     catch (IOException JavaDoc ex) {
721       return new ResponseEvent(this, null, pdu, null, target, ex);
722     }
723   }
724
725   /**
726    * Asynchronously sends a SET request <code>PDU</code> to the given target.
727    * The response is then returned by calling the supplied
728    * <code>ResponseListener</code> instance.
729    *
730    * @param pdu
731    * the PDU instance to send.
732    * @param target
733    * the Target instance representing the target SNMP engine where to send
734    * the <code>pdu</code>.
735    * @param userHandle
736    * an user defined handle that is returned when the request is returned
737    * via the <code>listener</code> object.
738    * @param listener
739    * a <code>ResponseListener</code> instance that is called when
740    * <code>pdu</code> is a confirmed PDU and the request is either answered
741    * or timed out.
742    * @throws IOException
743    * if the PDU cannot be sent to the target.
744    * @since 1.1
745    */

746   public void set(PDU pdu, Target target, Object JavaDoc userHandle,
747                   ResponseListener listener) throws IOException JavaDoc {
748     pdu.setType(PDU.SET);
749     send(pdu, target, userHandle, listener);
750   }
751
752   public ResponseEvent send(PDU pdu, Target target) throws IOException JavaDoc {
753     return send(pdu, target, null);
754   }
755
756   /**
757    * Sends a <code>PDU</code> to the given target and if the <code>PDU</code>
758    * is a confirmed request, then the received response is returned
759    * synchronously.
760    * @param pdu
761    * a <code>PDU</code> instance. When sending a SNMPv1 trap PDU, the
762    * supplied PDU instance must be a <code>PDUv1</code>. For all types of
763    * SNMPv3 messages, the supplied PDU instance has to be a
764    * <code>ScopedPDU</code> instance.
765    * @param target
766    * the Target instance representing the target SNMP engine where to send
767    * the <code>pdu</code>.
768    * @param transport
769    * specifies the <code>TransportMapping</code> to be used when sending
770    * the PDU. If <code>transport</code> is <code>null</code>, the associated
771    * message dispatcher will try to determine the transport mapping by the
772    * <code>target</code>'s address.
773    * @return
774    * the received response encapsulated in a <code>ResponseEvent</code>
775    * instance. To obtain the received response <code>PDU</code> call
776    * {@link ResponseEvent#getResponse()}. If the request timed out,
777    * that method will return <code>null</code>. If the sent <code>pdu</code>
778    * is an unconfirmed PDU (notification, response, or report), then
779    * <code>null</code> will be returned.
780    * @throws IOException
781    * if the message could not be sent.
782    * @see PDU
783    * @see ScopedPDU
784    * @see PDUv1
785    */

786   public ResponseEvent send(PDU pdu, Target target,
787                             TransportMapping transport) throws IOException JavaDoc {
788     if (!pdu.isConfirmedPdu()) {
789       sendMessage(pdu, target, transport, null);
790       return null;
791     }
792     SyncResponseListener syncResponse = new SyncResponseListener();
793     PendingRequest retryRequest = null;
794     synchronized (syncResponse) {
795       PduHandle handle = null;
796       PendingRequest request =
797           new PendingRequest(syncResponse, target, pdu, target, transport);
798       handle = sendMessage(pdu, target, transport, request);
799       try {
800         syncResponse.wait();
801         retryRequest = (PendingRequest) pendingRequests.remove(handle);
802         if (logger.isDebugEnabled()) {
803           logger.debug("Removed pending request with handle: "+handle);
804         }
805         request.setFinished();
806         request.cancel();
807       }
808       catch (InterruptedException JavaDoc iex) {
809         logger.warn(iex);
810         // ignore
811
}
812     }
813     if (retryRequest != null) {
814       synchronized (retryRequest) {
815         retryRequest.setFinished();
816         retryRequest.cancel();
817       }
818     }
819     return syncResponse.response;
820   }
821
822   public void send(PDU pdu, Target target,
823                    Object JavaDoc userHandle,
824                    ResponseListener listener) throws IOException JavaDoc {
825     send(pdu, target, null, userHandle, listener);
826   }
827
828   public void send(PDU pdu, Target target,
829                    TransportMapping transport,
830                    Object JavaDoc userHandle,
831                    ResponseListener listener) throws IOException JavaDoc {
832     if (!pdu.isConfirmedPdu()) {
833       sendMessage(pdu, target, transport, null);
834       return;
835     }
836     PendingRequest request =
837         new AsyncPendingRequest(listener, userHandle, pdu, target, transport);
838     sendMessage(pdu, target, transport, request);
839   }
840
841   /**
842    * Sends a <code>PDU</code> to the given target and returns the received
843    * response <code>PDU</code>.
844    * @param pdu
845    * a <code>PDU</code> instance. When sending a SNMPv1 trap PDU, the
846    * supplied PDU instance must be a <code>PDUv1</code>. For all types of
847    * SNMPv3 messages, the supplied PDU instance has to be a
848    * <code>ScopedPDU</code> instance.
849    * @param target
850    * the Target instance representing the target SNMP engine where to send
851    * the <code>pdu</code>.
852    * @return
853    * the received response <code>PDU</code> or <code>null</code>
854    * if the request timed out and if the PDU type of <code>pdu</code>
855    * is an unconfirmed PDU (i.e., trap or notification).
856    * @throws IOException
857    * if the message could not be sent.
858    * @see PDU
859    * @see ScopedPDU
860    * @see PDUv1
861    * @deprecated This method has been deprecated because it does not return
862    * the transport address of the entity (target) that sent the response.
863    * Please use {@link #send(PDU pdu, Target target)} instead. It returns
864    * a {@link ResponseEvent} object holding the response PDU and transport
865    * address of a successfully received response. This method will be supported
866    * until v2.0.
867    */

868   public PDU sendPDU(PDU pdu, Target target) throws IOException JavaDoc {
869     ResponseEvent e = send(pdu, target);
870     if (e != null) {
871       return e.getResponse();
872     }
873     // pdu sent is unconfirmed one
874
return null;
875   }
876
877   /**
878    * Asynchronously sends a <code>PDU</code> to the given target. The response
879    * is then returned by calling the supplied <code>ResponseListener</code>
880    * instance.
881    *
882    * @param pdu
883    * the PDU instance to send.
884    * @param target
885    * the Target instance representing the target SNMP engine where to send
886    * the <code>pdu</code>.
887    * @param userHandle
888    * an user defined handle that is returned when the request is returned
889    * via the <code>listener</code> object.
890    * @param listener
891    * a <code>ResponseListener</code> instance that is called when
892    * <code>pdu</code> is a confirmed PDU and the request is either answered
893    * or timed out.
894    * @deprecated Please use {@link #send(PDU pdu, Target target, Object
895    * userHandle, ResponseListener listener)} instead. It has exactly
896    * the same function but follows the new naming scheme. This method
897    * will be supported until v2.0.
898    * @throws IOException
899    * if the PDU could not be sent to the specified target.
900    */

901   public void sendPDU(PDU pdu,
902                       Target target,
903                       Object JavaDoc userHandle,
904                       ResponseListener listener) throws IOException JavaDoc {
905     send(pdu, target, userHandle, listener);
906   }
907
908   /**
909    * Actually sends a PDU to a target and returns a handle for the sent PDU.
910    * @param pdu
911    * the <code>PDU</code> instance to be sent.
912    * @param target
913    * a <code>Target</code> instance denoting the target SNMP entity.
914    * @param transport
915    * the (optional) transport mapping to be used to send the request.
916    * If <code>transport</code> is <code>null</code> a suitable transport
917    * mapping is determined from the <code>target</code> address.
918    * @param pduHandleCallback
919    * callback for newly created PDU handles before the request is sent out.
920    * @throws IOException
921    * if the transport fails to send the PDU or the if the message cannot
922    * be BER encoded.
923    * @return PduHandle
924    * that uniquely identifies the sent PDU for further reference.
925    */

926   protected PduHandle sendMessage(PDU pdu, Target target,
927                                   TransportMapping transport,
928                                   PduHandleCallback pduHandleCallback)
929       throws IOException JavaDoc
930   {
931     PduHandle handle = null;
932     if (target instanceof SecureTarget) {
933       SecureTarget secureTarget = (SecureTarget) target;
934       handle = messageDispatcher.sendPdu(transport,
935                                          secureTarget.getAddress(),
936                                          secureTarget.getVersion(),
937                                          secureTarget.getSecurityModel(),
938                                          secureTarget.getSecurityName().
939                                          getValue(),
940                                          secureTarget.getSecurityLevel(),
941                                          pdu, true, pduHandleCallback);
942     }
943     else if (target instanceof CommunityTarget) {
944       CommunityTarget communityTarget = (CommunityTarget) target;
945       int securityModel = SecurityModel.SECURITY_MODEL_SNMPv2c;
946       if (communityTarget.getVersion() == SnmpConstants.version1) {
947         securityModel = SecurityModel.SECURITY_MODEL_SNMPv1;
948       }
949       handle = messageDispatcher.sendPdu(transport,
950                                          communityTarget.getAddress(),
951                                          communityTarget.getVersion(),
952                                          securityModel,
953                                          communityTarget.getCommunity().
954                                          getValue(),
955                                          SecurityLevel.NOAUTH_NOPRIV,
956                                          pdu, true, pduHandleCallback);
957
958     }
959     return handle;
960   }
961
962   public void cancel(PDU request, ResponseListener listener) {
963     AsyncRequestKey key = new AsyncRequestKey(request, listener);
964     PduHandle pending = (PduHandle) asyncRequests.remove(key);
965     if (logger.isDebugEnabled()) {
966       logger.debug("Cancelling pending request with handle " + pending);
967     }
968     if (pending != null) {
969       PendingRequest pendingRequest =
970           (PendingRequest) pendingRequests.remove(pending);
971       if (pendingRequest != null) {
972         synchronized (pendingRequest) {
973           pendingRequest.setFinished();
974           pendingRequest.cancel();
975         }
976       }
977     }
978   }
979
980   /**
981    * Sets the local engine ID for the SNMP entity represented by this
982    * <code>Snmp</code> instance. This is a convenience method that sets
983    * the local engine ID in the associated <code>MPv3</code> and
984    * <code>USM</code>.
985    * @param engineID
986    * a byte array containing the local engine ID. The length and content
987    * has to comply with the constraints defined in the SNMP-FRAMEWORK-MIB.
988    * @param engineBoots
989    * the number of boots of this SNMP engine (zero based).
990    * @param engineTime
991    * the number of seconds since the value of engineBoots last changed.
992    * @see MPv3
993    * @see USM
994    */

995   public void setLocalEngine(byte[] engineID,
996                              int engineBoots,
997                              int engineTime) {
998     MPv3 mpv3 = getMPv3();
999     mpv3.setLocalEngineID(engineID);
1000    USM usm = (USM) mpv3.getSecurityModel(SecurityModel.SECURITY_MODEL_USM);
1001    usm.setLocalEngine(new OctetString(engineID), engineBoots, engineTime);
1002  }
1003
1004  /**
1005   * Gets the local engine ID if the MPv3 is available, otherwise a runtime
1006   * exception is thrown.
1007   * @return byte[]
1008   * the local engine ID.
1009   */

1010  public byte[] getLocalEngineID() {
1011    return getMPv3().getLocalEngineID();
1012  }
1013
1014  private MPv3 getMPv3() {
1015    MPv3 mpv3 = (MPv3) getMessageProcessingModel(MessageProcessingModel.MPv3);
1016    if (mpv3 == null) {
1017      throw new NoSuchElementException("MPv3 not available");
1018    }
1019    return mpv3;
1020  }
1021
1022  /**
1023   * Discovers the engine ID of the SNMPv3 entity denoted by the supplied
1024   * address. This method does not need to be called for normal operation,
1025   * because SNMP4J automatically discovers authoritative engine IDs and
1026   * also automatically synchronize engine time values.
1027   * <p>
1028   * <em>For this method to operate succesfully, the discover engine IDs
1029   * flag in {@link USM} must be <code>true</code> (which is the default).
1030   * </em>
1031   * @param address
1032   * an Address instance representing the transport address of the SNMPv3
1033   * entity for which its authoritative engine ID should be discovered.
1034   * @param timeout
1035   * the maximum time in milliseconds to wait for a response.
1036   * @return
1037   * a byte array containing the authoritative engine ID or <code>null</code>
1038   * if it could not be discovered.
1039   * @see USM#setEngineDiscoveryEnabled(boolean enableEngineDiscovery)
1040   */

1041  public byte[] discoverAuthoritativeEngineID(Address address, long timeout) {
1042    MPv3 mpv3 = getMPv3();
1043    // We need to remove the engine ID explicitly to be sure that it is updated
1044
OctetString engineID = mpv3.removeEngineID(address);
1045    // Now try to remove the engine as well
1046
if (engineID != null) {
1047      USM usm = getUSM();
1048      if (usm != null) {
1049        usm.removeEngineTime(engineID);
1050      }
1051    }
1052    ScopedPDU scopedPDU = new ScopedPDU();
1053    scopedPDU.setType(PDU.GET);
1054    SecureTarget target = new UserTarget();
1055    target.setTimeout(timeout);
1056    target.setAddress(address);
1057    try {
1058      send(scopedPDU, target);
1059      OctetString authoritativeEngineID = mpv3.getEngineID(address);
1060      if (authoritativeEngineID == null) {
1061        return null;
1062      }
1063      // we copy the byte array here, so we are sure nobody can modify the
1064
// internal cache.
1065
return new OctetString(authoritativeEngineID.getValue()).getValue();
1066    }
1067    catch (IOException JavaDoc ex) {
1068      logger.error(
1069          "IO error while trying to discover authoritative engine ID: " +
1070          ex);
1071      return null;
1072    }
1073  }
1074
1075  /**
1076   * Gets the User Based Security Model (USM). This is a convenience method
1077   * that uses the <code>SecurityModels</code> singleton to get the USM.
1078   * @return
1079   * an <code>USM</code> instance if available in the current SNMP4J
1080   * configuration, <code>null</code> otherwise.
1081   */

1082  public USM getUSM() {
1083    return (USM) SecurityModels.getInstance().getSecurityModel(
1084        new Integer32(SecurityModel.SECURITY_MODEL_USM));
1085  }
1086
1087  /**
1088   * Gets the message processing model for the supplied ID.
1089   * @param messageProcessingModel
1090   * a mesage processing model ID as defined in {@link MessageProcessingModel}.
1091   * @return MessageProcessingModel
1092   * a <code>MessageProcessingModel</code> if
1093   * <code>messageProcessingModel</code> has been registered with the
1094   * message dispatcher associated with this SNMP session.
1095   */

1096  public MessageProcessingModel getMessageProcessingModel(int
1097      messageProcessingModel) {
1098    return messageDispatcher.getMessageProcessingModel(messageProcessingModel);
1099  }
1100
1101  /**
1102   * Process an incoming request or notification PDU.
1103   *
1104   * @param event
1105   * a <code>CommandResponderEvent</code> with the decoded incoming PDU as
1106   * dispatched to this method call by the associated message dispatcher.
1107   */

1108  public void processPdu(CommandResponderEvent event) {
1109    PduHandle handle = event.getPduHandle();
1110    PDU pdu = event.getPDU();
1111    if (pdu.getType() == PDU.RESPONSE) {
1112      event.setProcessed(true);
1113      PendingRequest request;
1114      if (logger.isDebugEnabled()) {
1115        logger.debug("Looking up pending request with handle " + handle);
1116      }
1117      request = (PendingRequest) pendingRequests.get(handle);
1118      if (request == null) {
1119        if (logger.isWarnEnabled()) {
1120          logger.warn("Received response that cannot be matched to any " +
1121                      "outstanding request, address=" +
1122                      event.getPeerAddress() +
1123                      ", requestID=" + pdu.getRequestID());
1124        }
1125      }
1126      if (request == null) {
1127        logger.warn("Received response that cannot be matched to any " +
1128                    "outstanding request, address=" +
1129                    event.getPeerAddress() +
1130                    ", requestID=" + pdu.getRequestID());
1131      }
1132      else {
1133        request.listener.onResponse(new ResponseEvent(this,
1134            event.getPeerAddress(),
1135            request.pdu,
1136            pdu,
1137            request.userObject));
1138      }
1139    }
1140    else if (pdu.getType() == PDU.REPORT) {
1141      event.setProcessed(true);
1142      reportHandler.processReport(handle, event);
1143    }
1144    else {
1145      if (logger.isDebugEnabled()) {
1146        logger.debug("Fire process PDU event: " + event.toString());
1147      }
1148      fireProcessPdu(event);
1149    }
1150  }
1151
1152  class ReportProcessor implements ReportHandler {
1153
1154    public void processReport(PduHandle handle, CommandResponderEvent e) {
1155      PDU pdu = e.getPDU();
1156      logger.debug("Searching pending request with handle" + handle);
1157      PendingRequest request = (PendingRequest) pendingRequests.get(handle);
1158      if (request == null) {
1159        logger.warn("Unmatched report PDU received from " + e.getPeerAddress());
1160        return;
1161      }
1162      if (pdu.size() == 0) {
1163        logger.error("Illegal report PDU received from " + e.getPeerAddress() +
1164                     " missing report variable binding");
1165        return;
1166      }
1167      VariableBinding vb = pdu.get(0);
1168      if (vb == null) {
1169        logger.error("Received illegal REPORT PDU from " + e.getPeerAddress());
1170        return;
1171      }
1172      OID firstOID = vb.getOid();
1173      boolean resend = false;
1174      if (request.requestStatus < request.maxRequestStatus) {
1175        switch (request.requestStatus) {
1176          case 0:
1177            if (SnmpConstants.usmStatsUnknownEngineIDs.equals(firstOID)) {
1178              resend = true;
1179            }
1180            else if (SnmpConstants.usmStatsNotInTimeWindows.equals(firstOID)) {
1181              request.requestStatus++;
1182              resend = true;
1183            }
1184            break;
1185          case 1:
1186            if (SnmpConstants.usmStatsNotInTimeWindows.equals(firstOID)) {
1187              resend = true;
1188            }
1189            break;
1190        }
1191      }
1192      // if legal report PDU received, then resend request
1193
if (resend) {
1194        logger.debug("Send new request after report.");
1195        request.requestStatus++;
1196        try {
1197          // We need no callback here because we already have an equivalent
1198
// handle registered.
1199
PduHandle resentHandle =
1200              sendMessage(request.pdu, request.target, e.getTransportMapping(),
1201                          null);
1202          // make sure reference to handle is hold until request is finished,
1203
// because otherwise cache information may get lost (WeakHashMap)
1204
request.key = resentHandle;
1205        }
1206        catch (IOException JavaDoc iox) {
1207          logger.error("Failed to send message to " + request.target + ": " +
1208                       iox.getMessage());
1209          return;
1210        }
1211      }
1212      else {
1213        boolean intime;
1214        synchronized (request) {
1215          intime = request.cancel();
1216        }
1217        // remove pending request
1218
// (sync is not needed as request is already canceled)
1219
pendingRequests.remove(handle);
1220        if (intime) {
1221          // return report
1222
request.listener.onResponse(new ResponseEvent(this,
1223              e.getPeerAddress(),
1224              request.pdu,
1225              pdu,
1226              request.userObject));
1227        }
1228        else {
1229          // silently drop late report
1230
if (logger.isInfoEnabled()) {
1231            logger.info("Received late report from " +
1232                        e.getPeerAddress() +
1233                        " with request ID " + pdu.getRequestID());
1234          }
1235        }
1236      }
1237    }
1238  }
1239
1240
1241  /**
1242   * Removes a <code>CommandResponder</code> from this SNMP session.
1243   * @param listener
1244   * a previously added <code>CommandResponder</code> instance.
1245   */

1246  public synchronized void removeCommandResponder(CommandResponder listener) {
1247    if (commandResponderListeners != null &&
1248        commandResponderListeners.contains(listener)) {
1249      Vector v = (Vector) commandResponderListeners.clone();
1250      v.removeElement(listener);
1251      commandResponderListeners = v;
1252    }
1253  }
1254
1255  /**
1256   * Adds a <code>CommandResponder</code> to this SNMP session.
1257   * The command responder will then be informed about incoming SNMP PDUs of
1258   * any kind that are not related to any outstanding requests of this SNMP
1259   * session.
1260   *
1261   * @param listener
1262   * the <code>CommandResponder</code> instance to be added.
1263   */

1264  public synchronized void addCommandResponder(CommandResponder listener) {
1265    Vector v = (commandResponderListeners == null) ?
1266        new Vector(2) : (Vector) commandResponderListeners.clone();
1267    if (!v.contains(listener)) {
1268      v.addElement(listener);
1269      commandResponderListeners = v;
1270    }
1271  }
1272
1273  /**
1274   * Fires a <code>CommandResponderEvent</code> event to inform listeners about
1275   * a received PDU. If a listener has marked the event as processed further
1276   * listeners will not be informed about the event.
1277   * @param event
1278   * a <code>CommandResponderEvent</code>.
1279   */

1280  protected void fireProcessPdu(CommandResponderEvent event) {
1281    if (commandResponderListeners != null) {
1282      Vector listeners = commandResponderListeners;
1283      int count = listeners.size();
1284      for (int i = 0; i < count; i++) {
1285        ((CommandResponder) listeners.elementAt(i)).processPdu(event);
1286        // if event is marked as processed the event is not forwarded to
1287
// remaining listeners
1288
if (event.isProcessed()) {
1289          return;
1290        }
1291      }
1292    }
1293  }
1294
1295  /**
1296   * Gets the timeout model associated with this SNMP session.
1297   * @return
1298   * a TimeoutModel instance (never <code>null</code>).
1299   * @see #setTimeoutModel(TimeoutModel timeoutModel)
1300   */

1301  public TimeoutModel getTimeoutModel() {
1302    return timeoutModel;
1303  }
1304
1305  /**
1306   * Returns the report handler which is used internally to process reports
1307   * received from command responders.
1308   * @return
1309   * the <code>ReportHandler</code> instance.
1310   * @since 1.6
1311   */

1312  public ReportHandler getReportHandler() {
1313    return reportHandler;
1314  }
1315
1316  /**
1317   * Sets the timeout model for this SNMP session. The default timeout model
1318   * sends retries whenever the time specified by the <code>timeout</code>
1319   * parameter of the target has elapsed without a response beeing received for
1320   * the request. By specifying a different timeout model this behaviour can
1321   * be changed.
1322   * @param timeoutModel
1323   * a <code>TimeoutModel</code> instance (must not be <code>null</code>).
1324   */

1325  public void setTimeoutModel(TimeoutModel timeoutModel) {
1326    if (timeoutModel == null) {
1327      throw new NullPointerException JavaDoc("Timeout model cannot be null");
1328    }
1329    this.timeoutModel = timeoutModel;
1330  }
1331
1332  /**
1333   * Sets the report handler and overrides the default report handler.
1334   * @param reportHandler
1335   * a <code>ReportHandler</code> instance which must not be
1336   * <code>null</code>.
1337   * @since 1.6
1338   */

1339  public void setReportHandler(ReportHandler reportHandler) {
1340    if (reportHandler == null) {
1341      throw new IllegalArgumentException JavaDoc("ReportHandler must not be null");
1342    }
1343    this.reportHandler = reportHandler;
1344  }
1345
1346  class PendingRequest extends TimerTask implements PduHandleCallback {
1347
1348    private PduHandle key;
1349    protected int retryCount;
1350    protected ResponseListener listener;
1351    protected Object JavaDoc userObject;
1352
1353    protected PDU pdu;
1354    protected Target target;
1355    protected TransportMapping transport;
1356
1357    private int requestStatus = 0;
1358    // Maximum request status - allows to receive up to two reports and then
1359
// send the original request again. A value of 0 is used for discovery.
1360
private int maxRequestStatus = 2;
1361
1362    private volatile boolean finished = false;
1363
1364
1365    public PendingRequest(ResponseListener listener,
1366                          Object JavaDoc userObject,
1367                          PDU pdu,
1368                          Target target,
1369                          TransportMapping transport) {
1370      this.userObject = userObject;
1371      this.listener = listener;
1372      this.retryCount = target.getRetries();
1373      this.pdu = pdu;
1374      this.target = target;
1375      this.transport = transport;
1376    }
1377
1378    private PendingRequest(PendingRequest other) {
1379      this.userObject = other.userObject;
1380      this.listener = other.listener;
1381      this.retryCount = other.retryCount - 1;
1382      this.pdu = other.pdu;
1383      this.target = other.target;
1384      this.requestStatus = other.requestStatus;
1385    }
1386
1387    protected void registerRequest(PduHandle handle) {
1388
1389    }
1390
1391    public synchronized void pduHandleAssigned(PduHandle handle, Object JavaDoc pdu) {
1392      if (key == null) {
1393        key = handle;
1394        pendingRequests.put(handle, this);
1395        registerRequest(handle);
1396        if (logger.isDebugEnabled()) {
1397          logger.debug("Running pending "+
1398                       ((listener instanceof SyncResponseListener) ?
1399                        "sync" : "async")+
1400                       " request with handle " + handle+
1401                       " and retry count left "+retryCount);
1402        }
1403        long delay = timeoutModel.getRetryTimeout(target.getRetries() -
1404                                                  retryCount,
1405                                                  target.getRetries(),
1406                                                  target.getTimeout());
1407        if (!finished) {
1408          timer.schedule(this, delay);
1409        }
1410      }
1411    }
1412
1413    /**
1414     * Process retries of a pending request.
1415     */

1416    public synchronized void run() {
1417      try {
1418        if ((!finished) && (retryCount > 0)) {
1419          try {
1420            PendingRequest nextRetry = new PendingRequest(this);
1421            sendMessage(pdu, target, transport, nextRetry);
1422          }
1423          catch (IOException JavaDoc ex) {
1424            finished = true;
1425            logger.error("Failed to send SNMP message to " + target.toString() +
1426                         ": " +
1427                         ex.getMessage());
1428            messageDispatcher.releaseStateReference(target.getVersion(), key);
1429            listener.onResponse(new ResponseEvent(Snmp.this, null,
1430                                                  pdu, null, userObject,
1431                                                  ex));
1432          }
1433        }
1434        else if (!finished) {
1435          finished = true;
1436          pendingRequests.remove(key);
1437
1438          // request timed out
1439
if (logger.isDebugEnabled()) {
1440            logger.debug("Request timed out: " + key.getTransactionID());
1441          }
1442          messageDispatcher.releaseStateReference(target.getVersion(), key);
1443          listener.onResponse(new ResponseEvent(Snmp.this, null,
1444                                                pdu, null, userObject));
1445        }
1446      }
1447      catch (RuntimeException JavaDoc ex) {
1448        if (logger.isDebugEnabled()) {
1449          ex.printStackTrace();
1450        }
1451        logger.error("Failed to process pending request " + key +
1452                     " because " + ex.getMessage(), ex);
1453        throw ex;
1454      }
1455      catch (Error JavaDoc er) {
1456        if (logger.isDebugEnabled()) {
1457          er.printStackTrace();
1458        }
1459        logger.fatal("Failed to process pending request " + key +
1460                     " because " + er.getMessage(), er);
1461        throw er;
1462      }
1463    }
1464
1465    public synchronized boolean setFinished() {
1466      boolean currentState = finished;
1467      this.finished = true;
1468      return currentState;
1469    }
1470
1471    public void setMaxRepuestStatus(int maxRepuestStatus) {
1472      this.maxRequestStatus = maxRepuestStatus;
1473    }
1474
1475    public int getMaxRepuestStatus() {
1476      return maxRequestStatus;
1477    }
1478
1479  }
1480
1481  class AsyncPendingRequest extends PendingRequest {
1482    public AsyncPendingRequest(ResponseListener listener,
1483                               Object JavaDoc userObject,
1484                               PDU pdu,
1485                               Target target,
1486                               TransportMapping transport) {
1487      super(listener, userObject, pdu, target, transport);
1488    }
1489
1490    protected void registerRequest(PduHandle handle) {
1491      AsyncRequestKey key = new AsyncRequestKey(super.pdu, super.listener);
1492      asyncRequests.put(key, handle);
1493    }
1494  }
1495
1496  static class AsyncRequestKey {
1497    private PDU request;
1498    private ResponseListener listener;
1499
1500    public AsyncRequestKey(PDU request, ResponseListener listener) {
1501      this.request = request;
1502      this.listener = listener;
1503    }
1504
1505    /**
1506     * Indicates whether some other object is "equal to" this one.
1507     *
1508     * @param obj the reference object with which to compare.
1509     * @return <code>true</code> if this object is the same as the obj argument;
1510     * <code>false</code> otherwise.
1511     */

1512    public boolean equals(Object JavaDoc obj) {
1513      if (obj instanceof AsyncRequestKey) {
1514        AsyncRequestKey other = (AsyncRequestKey) obj;
1515        return (request.equals(other.request) && listener.equals(other.listener));
1516      }
1517      return false;
1518    }
1519
1520    public int hashCode() {
1521      return request.hashCode();
1522    }
1523  }
1524
1525  static class SyncResponseListener implements ResponseListener {
1526
1527    private ResponseEvent response = null;
1528
1529    public synchronized void onResponse(ResponseEvent event) {
1530      this.response = event;
1531      this.notify();
1532    }
1533
1534    public ResponseEvent getResponse() {
1535      return response;
1536    }
1537
1538  }
1539
1540  /**
1541   * The <code>NotificationDispatcher</code> dispatches traps, notifications,
1542   * and to registered listeners.
1543   *
1544   * @author Frank Fock
1545   * @version 1.6
1546   * @since 1.6
1547   */

1548  class NotificationDispatcher implements CommandResponder {
1549    // A mapping of transport addresses to transport mappings of notification
1550
// listeners
1551
private Hashtable notificationListeners = new Hashtable(10);
1552    private Hashtable notificationTransports = new Hashtable(10);
1553
1554    protected NotificationDispatcher() {
1555    }
1556
1557    public synchronized void addNotificationListener(Address listenAddress,
1558                                                     TransportMapping transport,
1559                                                     CommandResponder listener){
1560      notificationListeners.put(listenAddress, transport);
1561      notificationTransports.put(transport, listener);
1562    }
1563
1564    public synchronized boolean
1565        removeNotificationListener(Address listenAddress)
1566    {
1567      TransportMapping tm =
1568          (TransportMapping)notificationListeners.remove(listenAddress);
1569      if (tm == null) {
1570        return false;
1571      }
1572      tm.removeTransportListener(messageDispatcher);
1573      notificationTransports.remove(tm);
1574
1575      try {
1576        tm.close();
1577      }
1578      catch (IOException JavaDoc ex) {
1579        logger.error(ex);
1580        if (logger.isDebugEnabled()) {
1581          ex.printStackTrace();
1582        }
1583      }
1584      return true;
1585    }
1586
1587    public synchronized void closeAll() {
1588      notificationTransports.clear();
1589      for (Iterator it = notificationListeners.values().iterator();
1590           it.hasNext();) {
1591        TransportMapping tm = (TransportMapping) it.next();
1592        try {
1593          tm.close();
1594        }
1595        catch (IOException JavaDoc ex) {
1596          logger.error(ex);
1597          if (logger.isDebugEnabled()) {
1598            ex.printStackTrace();
1599          }
1600        }
1601      }
1602      notificationListeners.clear();
1603    }
1604
1605    public synchronized void processPdu(CommandResponderEvent event) {
1606      CommandResponder listener = (CommandResponder)
1607          notificationTransports.get(event.getTransportMapping());
1608      if ((event.getPDU() != null) &&
1609          (event.getPDU().getType() == PDU.INFORM)) {
1610        // try to send INFORM response
1611
try {
1612          sendInformResponse(event);
1613        }
1614        catch (MessageException mex) {
1615          if (logger.isWarnEnabled()) {
1616            logger.warn("Failed to send response on INFORM PDU event (" +
1617                        event + "): " + mex.getMessage());
1618          }
1619        }
1620      }
1621      if (listener != null) {
1622        listener.processPdu(event);
1623      }
1624    }
1625
1626    /**
1627     * Sends a RESPONSE PDU to the source address of a INFORM request.
1628     * @param event
1629     * the <code>CommandResponderEvent</code> with the INFORM request.
1630     * @throws
1631     * MessageException if the response could not be created and sent.
1632     */

1633    protected void sendInformResponse(CommandResponderEvent event) throws
1634        MessageException {
1635      PDU responsePDU = (PDU) event.getPDU().clone();
1636      responsePDU.setType(PDU.RESPONSE);
1637      responsePDU.setErrorStatus(PDU.noError);
1638      responsePDU.setErrorIndex(0);
1639      messageDispatcher.returnResponsePdu(event.getMessageProcessingModel(),
1640                                          event.getSecurityModel(),
1641                                          event.getSecurityName(),
1642                                          event.getSecurityLevel(),
1643                                          responsePDU,
1644                                          event.getMaxSizeResponsePDU(),
1645                                          event.getStateReference(),
1646                                          new StatusInformation());
1647    }
1648  }
1649
1650}
1651
Popular Tags