View Javadoc
1   // NAME
2   //      $RCSfile: AbstractSnmpContext.java,v $
3   // DESCRIPTION
4   //      [given below in javadoc format]
5   // DELTA
6   //      $Revision: 3.33 $
7   // CREATED
8   //      $Date: 2009/03/05 12:48:04 $
9   // COPYRIGHT
10  //      Westhawk Ltd
11  // TO DO
12  //
13  
14  /*
15   * Copyright (C) 2000 - 2006 by Westhawk Ltd
16   * <a href="www.westhawk.co.uk">www.westhawk.co.uk</a>
17   *
18   * Permission to use, copy, modify, and distribute this software
19   * for any purpose and without fee is hereby granted, provided
20   * that the above copyright notices appear in all copies and that
21   * both the copyright notice and this permission notice appear in
22   * supporting documentation.
23   * This software is provided "as is" without express or implied
24   * warranty.
25   * author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
26   */
27  
28  package uk.co.westhawk.snmp.stack;
29  
30  /*-
31   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
32   * SNMP Java Client
33   * ჻჻჻჻჻჻
34   * Copyright 2023 MetricsHub, Westhawk
35   * ჻჻჻჻჻჻
36   * This program is free software: you can redistribute it and/or modify
37   * it under the terms of the GNU Lesser General Public License as
38   * published by the Free Software Foundation, either version 3 of the
39   * License, or (at your option) any later version.
40   *
41   * This program is distributed in the hope that it will be useful,
42   * but WITHOUT ANY WARRANTY; without even the implied warranty of
43   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
44   * GNU General Lesser Public License for more details.
45   *
46   * You should have received a copy of the GNU General Lesser Public
47   * License along with this program.  If not, see
48   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
49   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
50   */
51  
52  import java.io.*;
53  import java.util.*;
54  import uk.co.westhawk.snmp.net.*;
55  import uk.co.westhawk.snmp.event.*;
56  import uk.co.westhawk.snmp.util.*;
57  
58  /**
59   * This class contains the abstract SNMP context that is needed by every
60   * Pdu to send a SNMP v1, v2c or v3 request.
61   * The context also provides functionality to receive PDUs.
62   *
63   * <p>
64   * <code>destroy()</code> should be called when the context is no longer
65   * used. This is the only way the threads will be stopped and garbage
66   * collected.
67   * </p>
68   *
69   * @see SnmpContext
70   * @see SnmpContextv2c
71   * @see SnmpContextv3
72   *
73   * @author <a href="mailto:snmp@westhawk.co.uk">Tim Panton</a>
74   * @version $Revision: 3.33 $ $Date: 2009/03/05 12:48:04 $
75   */
76  public abstract class AbstractSnmpContext extends Object
77          implements SnmpContextBasisFace, Runnable, RawPduListener {
78      private static final String version_id = "@(#)$Id: AbstractSnmpContext.java,v 3.33 2009/03/05 12:48:04 birgita Exp $ Copyright Westhawk Ltd";
79  
80      private ContextSocketFace soc;
81      private Transmitter[] transmitters;
82      private Pdu[] pdus;
83      private Thread me;
84      private String basename;
85      private volatile boolean stopRequested;
86      // thanks to Nick Sheen nsheen@tippingpoint.com for pointing out that volatile
87      // is needed here
88  
89      protected String typeSocket;
90      protected String hostname;
91      protected String bindAddr;
92      protected int hostPort;
93      protected int maxRecvSize;
94      protected boolean isDestroyed;
95      protected boolean anyPduExpectingResponse = false;
96      protected RequestPduReceivedSupport pduSupport;
97      protected TrapReceivedSupport trapSupport;
98  
99      /**
100      * Processes an incoming response. Has to be overload by each context.
101      * This is called in the run() method.
102      *
103      * @see #run
104      */
105     protected abstract void processIncomingResponse(ByteArrayInputStream in)
106             throws DecodingException, IOException;
107 
108     /**
109      * Encodes a PDU. This is for internal use only and should
110      * NOT be called by the developer.
111      * This is called by the the PDU itself and is added to the interface to
112      * cover the different kind of Contexts.
113      * Has to be overload by each context.
114      *
115      * @param msg_type The message type
116      * @param rId      The message id
117      * @param errstat  The error status
118      * @param errind   The error index
119      * @param ve       The varbind list
120      * @param obj      Additional object (only used in SNMPv3)
121      * @return The encoded packet
122      */
123     public abstract byte[] encodePacket(byte msg_type, int rId, int errstat,
124             int errind, Enumeration ve, Object obj)
125             throws IOException, EncodingException;
126 
127     /**
128      * Processes an incoming pdu (but not a response). Has to be overload by each
129      * context.
130      * 
131      * @see #rawPduReceived
132      */
133     public abstract Pdu processIncomingPdu(byte[] message) throws DecodingException, IOException;
134 
135     /**
136      * Returns the SNMP version of this context. Has to be overload by each
137      * context.
138      */
139     public abstract int getVersion();
140 
141     /**
142      * Constructor.
143      * The Standard socket type will be used.
144      *
145      * @param host The host to which the Pdu will be sent
146      * @param port The port where the SNMP server will be
147      * @see SnmpContextBasisFace#STANDARD_SOCKET
148      */
149     protected AbstractSnmpContext(String host, int port) throws IOException {
150         this(host, port, null, STANDARD_SOCKET);
151     }
152 
153     /**
154      * Constructor.
155      * Parameter typeSocketA should be either STANDARD_SOCKET, TCP_SOCKET or a
156      * fully qualified classname.
157      *
158      * @param host        The host to which the Pdu will be sent
159      * @param port        The port where the SNMP server will be
160      * @param typeSocketA The type of socket to use.
161      *
162      * @see SnmpContextBasisFace#STANDARD_SOCKET
163      * @see SnmpContextBasisFace#TCP_SOCKET
164      */
165     protected AbstractSnmpContext(String host, int port, String typeSocketA)
166             throws IOException {
167         this(host, port, null, typeSocketA);
168     }
169 
170     /**
171      * Constructor.
172      *
173      * If bindAddress is null, then the system will pick up a valid local
174      * address to bind the socket.
175      *
176      * The typeSocket will indicate which type of socket to use. This way
177      * different handlers can be provided.
178      * It should be either STANDARD_SOCKET, TCP_SOCKET or a
179      * fully qualified classname.
180      *
181      * @param host        The host to which the Pdu will be sent
182      * @param port        The port where the SNMP server will be
183      * @param bindAddress The local address the server will bind to
184      * @param typeSocketA The type of socket to use.
185      *
186      * @exception IOException Thrown when the socket cannot be
187      *                        created.
188      *
189      * @see SnmpContextBasisFace#STANDARD_SOCKET
190      * @see SnmpContextBasisFace#TCP_SOCKET
191      * @since 4_14
192      */
193     protected AbstractSnmpContext(String host, int port, String bindAddress, String typeSocketA)
194             throws IOException {
195         pdus = new Pdu[MAXPDU];
196         hostname = host;
197         hostPort = port;
198         bindAddr = bindAddress;
199         typeSocket = typeSocketA;
200         transmitters = new Transmitter[MAXPDU];
201         basename = host + "_" + port + "_" + bindAddress;
202         trapSupport = new TrapReceivedSupport(this);
203         pduSupport = new RequestPduReceivedSupport(this);
204 
205         isDestroyed = false;
206         stopRequested = false;
207         maxRecvSize = MSS;
208 
209         soc = getSocket(typeSocket);
210         if (soc != null) {
211             soc.create(hostname, hostPort, bindAddr);
212             if (AsnObject.debug > 12) {
213                 System.out.println(getClass().getName()
214                         + ": soc.getLocalSocketAddress() = "
215                         + soc.getLocalSocketAddress());
216                 System.out.println(getClass().getName()
217                         + ": soc.getRemoteSocketAddress() = "
218                         + soc.getRemoteSocketAddress());
219             }
220         }
221     }
222 
223     /**
224      * Returns a new socket, based on a particular type.
225      * Parameter type is first compare do STANDARD_SOCKET and TCP_SOCKET. If
226      * that doesn't match, type is assumed to be a fully qualified classname.
227      *
228      * @see SnmpContextBasisFace#STANDARD_SOCKET
229      * @see SnmpContextBasisFace#TCP_SOCKET
230      */
231     static ContextSocketFace getSocket(String type) throws IOException {
232         ContextSocketFace sf = null;
233         if (type != null) {
234             String className = null;
235             if (type.equals(STANDARD_SOCKET)) {
236                 className = "uk.co.westhawk.snmp.net.StandardSocket";
237             } else if (type.equals(TCP_SOCKET)) {
238                 className = "uk.co.westhawk.snmp.net.TCPSocket";
239             } else {
240                 className = type;
241             }
242 
243             try {
244                 Class cl = Class.forName(className);
245                 Object obj = cl.newInstance();
246                 sf = (ContextSocketFace) obj;
247             } catch (ClassNotFoundException exc) {
248                 String str = "AbstractSnmpContext.getSocket(): ClassNotFound problem " + exc.getMessage() + ", type="
249                         + type;
250                 throw (new IOException(str));
251             } catch (InstantiationException exc) {
252                 String str = "AbstractSnmpContext.getSocket(): Instantiation problem " + exc.getMessage() + ", type="
253                         + type;
254                 throw (new IOException(str));
255             } catch (IllegalAccessException exc) {
256                 String str = "AbstractSnmpContext.getSocket(): IllegalAccess problem " + exc.getMessage() + ", type="
257                         + type;
258                 throw (new IOException(str));
259             } catch (ClassCastException exc) {
260                 String str = "AbstractSnmpContext.getSocket(): ClassCast problem " + exc.getMessage() + ", type="
261                         + type;
262                 throw (new IOException(str));
263             }
264 
265             if (sf == null) {
266                 String str = "AbstractSnmpContext.getSocket(): Cannot create socket " + type;
267                 throw (new IOException(str));
268             } else {
269                 if (AsnObject.debug > 12) {
270                     System.out.println("AbstractSnmpContext.getSocket(): New socket " + sf.getClass().getName());
271                 }
272             }
273         }
274         return sf;
275     }
276 
277     public String getHost() {
278         return hostname;
279     }
280 
281     /**
282      * Returns the IP address string
283      * aaa.bbb.ccc.ddd (IPv4) or a:b:c:d:e:f:g:h (IPv6)
284      * of the host.
285      *
286      * @return The IP address of the host
287      * @deprecated As of 4_14, use {@link #getSendToHostAddress()}
288      */
289     public String getHostAddress() {
290         return getSendToHostAddress();
291     }
292 
293     public String getSendToHostAddress() {
294         String res = "";
295         if (soc != null) {
296             res = soc.getSendToHostAddress();
297         }
298         return res;
299     }
300 
301     public String getReceivedFromHostAddress() {
302         String res = "";
303         if (soc != null) {
304             res = soc.getReceivedFromHostAddress();
305         }
306         return res;
307     }
308 
309     public int getPort() {
310         return hostPort;
311     }
312 
313     public String getBindAddress() {
314         return bindAddr;
315     }
316 
317     public String getTypeSocket() {
318         return typeSocket;
319     }
320 
321     /**
322      * Returns the maximum number of bytes this context will read from the
323      * socket. By default this will be set to <code>MSS</code> (i.e. 1300).
324      *
325      * @since 4_12
326      * @see SnmpContextBasisFace#MSS
327      * @see #setMaxRecvSize(int)
328      * @return The number
329      */
330     public int getMaxRecvSize() {
331         return maxRecvSize;
332     }
333 
334     /**
335      * Sets the maximum number of bytes this context will read from the
336      * socket. By default this will be set to <code>MSS</code> (i.e. 1300).
337      * The default size seems a reasonable size. The problem usually occurs
338      * when sending Bulk requests.
339      *
340      * <p>
341      * If a packet arrives that is bigger than the maximum size of received
342      * bytes, the stack will try to decode it nevertheless. The usual
343      * error that will occur is:
344      * </p>
345      * 
346      * <pre>
347      * Error message: "Incorrect packet. No of bytes received less than packet length."
348      * </pre>
349      *
350      * <p>
351      * Although UDP datagrams can be fragmented (fragmentation is part of
352      * the network layer (IP), not the transport layer (UDP/TCP)), some
353      * firewalls reject incoming fragments. Therefor it is best not to set
354      * maxRecvSize higher than the largest packet size you can get through
355      * your network topology.
356      * </p>
357      *
358      * <p>
359      * Thanks to Pete Kazmier (pete@kazmier.com) for the suggestion.
360      * </p>
361      *
362      * <em style="color:red;">
363      * Note, this property is NOT supported in any of the SNMPContextXXPool
364      * classes.
365      * </em>
366      *
367      * @since 4_12
368      * @see SnmpContextBasisFace#MSS
369      * @see #getMaxRecvSize()
370      * @param no The new number
371      */
372     public void setMaxRecvSize(int no) {
373         maxRecvSize = no;
374     }
375 
376     /**
377      * Returns the thread usage of the AbstractSnmpContext.
378      * It returns a String in the form of <code>=PO=QR--------------0</code>.
379      *
380      * <p>
381      * The String represents the array of transmitters.
382      * Each character represents a transmitter slot.
383      * The transmitters form a thread pool of a maximum size, MAXPDU.
384      * Each transmitter is used to wait for one PDU response at a given
385      * moment in time.
386      * When the response is received the transmitter will stop running, but
387      * is not destroyed. It will be reused.
388      * </p>
389      *
390      * <p>
391      * Meaning of each character:
392      * </p>
393      * <ul>
394      * <li><code>-</code> a transmitter slot has not yet been allocated a
395      * thread</li>
396      * <li><code>=</code> there is a thread but it is idle</li>
397      * <li><code>A->Z</code> the thread is transmitting a Pdu</li>
398      * <li>
399      * The last character represents the context's recv thread:
400      * <ul>
401      * <li><code>0</code> there isn't one</li>
402      * <li><code>1</code> it exists but isn't running</li>
403      * <li><code>2</code> it exists and is alive.</li>
404      * </ul>
405      * </li>
406      * </ul>
407      *
408      * @since 4_12
409      * @return The thread usage of the AbstractSnmpContext
410      */
411     public String getDebugString() {
412         char[] cret = new char[MAXPDU + 1];
413         for (int i = 0; i < MAXPDU; i++) {
414             if (transmitters[i] != null) {
415                 if (pdus[i] != null) {
416                     cret[i] = (char) ('A' + (pdus[i].getReqId() % 26));
417                 } else {
418                     cret[i] = '=';
419                 }
420             } else {
421                 cret[i] = '-';
422             }
423         }
424 
425         char res = '0';
426         if (me != null) {
427             res++;
428             if (me.isAlive()) {
429                 res++;
430             }
431         }
432         cret[MAXPDU] = res;
433 
434         return new String(cret);
435     }
436 
437     /**
438      * This method will stop the thread.
439      * All transmitters, PDUs in flight and traplisteners will be removed
440      * when run() finishes.
441      *
442      * <p>
443      * It closes the socket.
444      * The thread will actually stop/finish when the run() finishes. Since
445      * the socket is closed, the run() will fall through almost instantly.
446      * </p>
447      *
448      * <p>
449      * Note: The thread(s) will not die immediately; this will take about
450      * half a minute.
451      * </p>
452      *
453      * @see ListeningContext#destroy()
454      * @see ListeningContextPool#destroyPool()
455      */
456     public synchronized void destroy() {
457         if (isDestroyed == false) {
458             stopRequested = true;
459             if (AsnObject.debug > 12) {
460                 System.out.println(getClass().getName() + ".destroy(): Closing socket ");
461             }
462             soc.close();
463             isDestroyed = true;
464 
465             // If run() has been started, then it will destroy the
466             // transmitter threads when it finishes. Otherwise they must be
467             // destroyed here.
468             if (me == null) {
469                 freeTransmitters();
470             }
471         }
472     }
473 
474     public boolean isDestroyed() {
475         return isDestroyed;
476     }
477 
478     /**
479      * This method will stop the thread.
480      * All transmitters, PDUs in flight and traplisteners will be removed
481      * when run() finishes.
482      * <p>
483      * It does NOT close the socket.
484      * The thread will actually stop/finish when the run() finishes. That is when
485      * a packet arrives on the socket or when the socket times out.
486      * </p>
487      *
488      * <p>
489      * We have deprecated this method since there is no point in stopping
490      * the context, but not destroying it. The context cannot start again anyway.
491      * The difference between destroy() and stop() was not very clear.
492      * </p>
493      *
494      * @deprecated As of version 4_12, should use {@link #destroy()}
495      * @see #destroy()
496      */
497     public synchronized void stop() {
498         stopRequested = true;
499     }
500 
501     /**
502      * We wait for any incoming packets. After receiving one, decode
503      * the packet into an Pdu. The Pdu will notify the observers waiting
504      * for an response.
505      *
506      * <p>
507      * Thanks to Chris Barlock &lt;barlock@us.ibm.com&gt; who reported a
508      * NullPointerException in run() on variable 'me' and introduced the
509      * variable stopRequested.
510      * </p>
511      */
512     public void run() {
513         // while It is visible
514         while (!stopRequested) {
515             // block for incoming packets
516             me.yield();
517             try {
518                 if (stopRequested) {
519                     break;
520                 }
521 
522                 StreamPortItem item = soc.receive(maxRecvSize);
523                 ByteArrayInputStream in = item.getStream();
524 
525                 if (AsnObject.debug > 10) {
526                     int nb = in.available();
527                     byte[] bu = new byte[nb];
528                     in.read(bu);
529                     in.reset();
530 
531                     SnmpUtilities.dumpBytes(getClass().getName()
532                             + ".run(): Received from "
533                             + item.getHostAddress()
534                             + ", from port " + item.getHostPort()
535                             + ": ", bu);
536                 }
537                 processIncomingResponse(in);
538             } catch (IOException exc) {
539                 if (exc instanceof InterruptedIOException) {
540                     if (AsnObject.debug > 15) {
541                         System.out.println(getClass().getName() + ".run(): Idle recv " + exc.getMessage());
542                     }
543                 } else if (exc instanceof java.net.SocketException) {
544                     if (AsnObject.debug > 15) {
545                         System.out.println(getClass().getName() + ".run(): SocketException " + exc.getMessage());
546                     }
547                 } else {
548                     if (AsnObject.debug > 0) {
549                         System.out.println(getClass().getName() + ".run(): "
550                                 + exc.getClass().getName() + " " + exc.getMessage());
551                         exc.printStackTrace();
552                     }
553                 }
554             } catch (DecodingException exc) {
555                 if (AsnObject.debug > 1) {
556                     System.out.println(getClass().getName() + ".run(): DecodingException: " + exc.getMessage());
557                 }
558             } catch (Exception exc) {
559                 if (AsnObject.debug > 1) {
560                     System.out.println(getClass().getName() + ".run(): Exception: " + exc.getMessage());
561                     exc.printStackTrace();
562                 }
563             } catch (Error err) {
564                 if (AsnObject.debug > 1) {
565                     System.out.println(getClass().getName() + ".run(): Error: " + err.getMessage());
566                     err.printStackTrace();
567                 }
568             }
569         }
570 
571         freeTransmitters();
572 
573         trapSupport.empty();
574         pduSupport.empty();
575 
576         // This used to actually create a listener. I do think this bug
577         // has been fixed, since no socket will be created in
578         // ListeningContextPool, unless a listener has been added.
579         ListeningContextPool lcontext = new ListeningContextPool(ListeningContextFace.DEFAULT_TRAP_PORT, bindAddr,
580                 typeSocket);
581         lcontext.removeRawPduListenerFromPool(this);
582 
583         me = null;
584         soc = null;
585     }
586 
587     /*
588      * By moving activate() from the constructor to here, the parameter
589      * maxRecvSize, changed in setMaxRecvSize(), gets a chance to actually
590      * change before run() starts.
591      * Thanks to Dave Hunt <dave.hunt@csipros.com> who reported this
592      * problem.
593      */
594     public synchronized void sendPacket(byte[] p) {
595         if (isDestroyed == false) {
596             activate();
597             try {
598                 if (AsnObject.debug > 10) {
599                     SnmpUtilities.dumpBytes("Sending to "
600                             + soc.getSendToHostAddress() + ": ", p);
601                 }
602 
603                 // Seen it throw an "java.io.IOException: Invalid argument"
604                 // when the bind address was wrong, i.e. the packet reach
605                 // the host over the interface
606                 soc.send(p);
607             } catch (IOException exc) {
608                 if (AsnObject.debug > 0) {
609                     System.out.println(getClass().getName() + ".sendPacket(): "
610                             + exc.getClass().getName()
611                             + " " + exc.getMessage());
612                     exc.printStackTrace();
613                 }
614             }
615         }
616     }
617 
618     Pdu getPdu(Integer ReqId) {
619         return getPdu(ReqId.intValue());
620     }
621 
622     Pdu getPdu(int rid) {
623         Pdu ret = null;
624         for (int i = 0; i < MAXPDU; i++) {
625             if ((pdus[i] != null) && (pdus[i].getReqId() == rid)) {
626                 ret = pdus[i];
627                 break;
628             }
629         }
630         return ret;
631     }
632 
633     public synchronized boolean removePdu(int rid) {
634         boolean ret = false;
635         for (int i = 0; i < MAXPDU; i++) {
636             if ((pdus[i] != null) && (pdus[i].getReqId() == rid)) {
637                 pdus[i] = null;
638                 ret = true;
639                 break;
640             }
641         }
642         return ret;
643     }
644 
645     public synchronized boolean addPdu(Pdu p)
646             throws IOException, PduException {
647         boolean done = false;
648         if (isDestroyed == true) {
649             throw new EncodingException("Context can no longer be used, since it is already destroyed");
650         } else {
651             // I only want to start the receive thread when any of the
652             // context's PDUs is actually expecting a response. See activate().
653             if (anyPduExpectingResponse == false) {
654                 anyPduExpectingResponse = p.isExpectingResponse();
655             }
656             for (int i = 0; i < MAXPDU; i++) {
657                 if (pdus[i] == null) {
658                     pdus[i] = p;
659                     pdus[i].setTrans(getTrans(i));
660                     done = true;
661                     break;
662                 }
663             }
664         }
665         return done;
666     }
667 
668     public void addTrapListener(TrapListener l) throws IOException {
669         addTrapListener(l, ListeningContextFace.DEFAULT_TRAP_PORT);
670     }
671 
672     public void removeTrapListener(TrapListener l) throws IOException {
673         removeTrapListener(l, ListeningContextFace.DEFAULT_TRAP_PORT);
674     }
675 
676     public void addTrapListener(TrapListener l, int port)
677             throws IOException {
678         ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
679         addTrapListener(l, lcontext);
680     }
681 
682     public void removeTrapListener(TrapListener l, int port)
683             throws IOException {
684         ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
685         removeTrapListener(l, lcontext);
686     }
687 
688     public void addTrapListener(TrapListener l, ListeningContextPool lcontext)
689             throws IOException {
690         trapSupport.addTrapListener(l);
691         lcontext.addRawPduListener(this);
692     }
693 
694     public void removeTrapListener(TrapListener l, ListeningContextPool lcontext)
695             throws IOException {
696         trapSupport.removeTrapListener(l);
697         if (trapSupport.getListenerCount() == 0
698                 &&
699                 pduSupport.getListenerCount() == 0) {
700             lcontext.removeRawPduListener(this);
701         }
702     }
703 
704     public void addRequestPduListener(RequestPduListener l)
705             throws IOException {
706         addRequestPduListener(l, SnmpContextBasisFace.DEFAULT_PORT);
707     }
708 
709     public void removeRequestPduListener(RequestPduListener l)
710             throws IOException {
711         removeRequestPduListener(l, SnmpContextBasisFace.DEFAULT_PORT);
712     }
713 
714     public void addRequestPduListener(RequestPduListener l, int port) throws IOException {
715         ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
716         addRequestPduListener(l, lcontext);
717     }
718 
719     public void removeRequestPduListener(RequestPduListener l, int port) throws IOException {
720         ListeningContextPool lcontext = new ListeningContextPool(port, bindAddr, typeSocket);
721         removeRequestPduListener(l, lcontext);
722     }
723 
724     public void addRequestPduListener(RequestPduListener l, ListeningContextPool lcontext) throws IOException {
725         pduSupport.addRequestPduListener(l);
726         lcontext.addRawPduListener(this);
727     }
728 
729     public void removeRequestPduListener(RequestPduListener l, ListeningContextPool lcontext) throws IOException {
730         pduSupport.removeRequestPduListener(l);
731         if (trapSupport.getListenerCount() == 0
732                 &&
733                 pduSupport.getListenerCount() == 0) {
734             lcontext.removeRawPduListener(this);
735         }
736     }
737 
738     /**
739      * Invoked when an undecoded pdu is received.
740      * First the version and the hostaddress are checked, if correct
741      * an attempt is made to decode the pdu.
742      * When successful the original event is consumed and a decoded pdu event
743      * is passed on the listeners.
744      *
745      * @see RawPduReceivedSupport#fireRawPduReceived
746      * @see RequestPduReceivedSupport#fireRequestPduReceived
747      * @see TrapReceivedSupport#fireTrapReceived
748      */
749     public void rawPduReceived(RawPduEvent evt) {
750         String hostAddress = evt.getHostAddress();
751         int version = evt.getVersion();
752         if (version == this.getVersion()) {
753             if (hostAddress != null && hostAddress.equals(this.getSendToHostAddress()) == true) {
754                 byte[] message = evt.getMessage();
755                 Pdu pdu = null;
756                 try {
757                     pdu = processIncomingPdu(message);
758                     if (pdu != null) {
759                         evt.consume();
760                         int port = evt.getHostPort();
761 
762                         if (pdu.getMsgType() == SnmpConstants.TRP_REQ_MSG
763                                 ||
764                                 pdu.getMsgType() == SnmpConstants.TRPV2_REQ_MSG) {
765                             trapSupport.fireTrapReceived(pdu, port);
766                         } else {
767                             pduSupport.fireRequestPduReceived(pdu, port);
768                         }
769                     } else {
770                         // somehow the context matches, but the pdu type is
771                         // not recognised.
772                     }
773                 } catch (DecodingException exc) {
774                     if (AsnObject.debug > 2) {
775                         System.out.println(
776                                 getClass().getName() + ".rawPduReceived(): DecodingException: " + exc.getMessage());
777                     }
778                 } catch (IOException exc) {
779                     if (AsnObject.debug > 0) {
780                         System.out.println(getClass().getName() + ".rawPduReceived(): IOException " + exc.getMessage());
781                     }
782                 }
783 
784             } else {
785                 if (AsnObject.debug > 5) {
786                     System.out.println(getClass().getName() + ".rawPduReceived(): "
787                             + "Pdu host (" + hostAddress
788                             + "), does not correspond with context host ("
789                             + this.getSendToHostAddress() + ")");
790                 }
791             }
792         } else {
793             if (AsnObject.debug > 5) {
794                 String theirs = SnmpUtilities.getSnmpVersionString(version);
795                 String ours = SnmpUtilities.getSnmpVersionString(this.getVersion());
796                 System.out.println(getClass().getName() + ".rawPduReceived(): "
797                         + "Pdu version " + theirs
798                         + ", does not correspond with context version "
799                         + ours);
800             }
801         }
802     }
803 
804     Transmitter getTrans(int i) {
805         if (transmitters[i] == null) {
806             transmitters[i] = new Transmitter(basename + "_v" + getVersion() + "_Trans" + i);
807         }
808         return transmitters[i];
809     }
810 
811     /**
812      * Creates and starts the Receive thread that allows this context to
813      * receive packets.
814      * Subclasses may override this to adjust the threading behaviour.
815      *
816      * @see PassiveSnmpContext#activate()
817      * @see PassiveSnmpContextv2c#activate()
818      */
819     protected void activate() {
820         // Only start the thread when 'me' is null (i.e. no thread is running)
821         // AND when anyPduExpectingResponse is true.
822         // This way a context that only sends (for example) traps, will not
823         // start a listing thread.
824         if (me == null && anyPduExpectingResponse == true) {
825             me = new Thread(this, basename + "_v" + getVersion() + "_Receive");
826             me.setPriority(me.MAX_PRIORITY);
827             me.start();
828         }
829     }
830 
831     /**
832      * Frees the transmitters.
833      *
834      * @see #run()
835      * @see #destroy()
836      * @since 5_1
837      */
838     // In version 5_0, this code lived in run().
839     // Thanks to Vincent Deconinck <vdeconinck@tiscalinet.be>
840     protected void freeTransmitters() {
841         for (int i = 0; i < MAXPDU; i++) {
842             if (transmitters[i] != null) {
843                 transmitters[i].destroy();
844                 transmitters[i] = null;
845             }
846             if (pdus[i] != null) {
847                 pdus[i] = null;
848             }
849         }
850     }
851 
852     /**
853      * Returns a clone of this SnmpContext.
854      *
855      * @since 4_14
856      * @exception CloneNotSupportedException Thrown when the constructor
857      *                                       generates an IOException or when in one
858      *                                       of the Pool classes.
859      */
860     public abstract Object clone() throws CloneNotSupportedException;
861 
862     /**
863      * Returns the hash key. This key is built out of all properties.
864      *
865      * @since 4_14
866      * @return The hash key
867      */
868     public abstract String getHashKey();
869 
870 }