org.snmp4j.Snmp Maven / Gradle / Ivy
Show all versions of snmp4j Show documentation
/*_############################################################################
_##
_## SNMP4J - Snmp.java
_##
_## Copyright (C) 2003-2009 Frank Fock and Jochen Katz (SNMP4J.org)
_##
_## Licensed under the Apache License, Version 2.0 (the "License");
_## you may not use this file except in compliance with the License.
_## You may obtain a copy of the License at
_##
_## http://www.apache.org/licenses/LICENSE-2.0
_##
_## Unless required by applicable law or agreed to in writing, software
_## distributed under the License is distributed on an "AS IS" BASIS,
_## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
_## See the License for the specific language governing permissions and
_## limitations under the License.
_##
_##########################################################################*/
package org.snmp4j;
import java.io.*;
import java.util.*;
import org.snmp4j.event.*;
import org.snmp4j.log.*;
import org.snmp4j.mp.*;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.*;
import org.snmp4j.util.*;
/**
* The Snmp
class is the core of SNMP4J. It provides functions to
* send and receive SNMP PDUs. All SNMP PDU types can be send. Confirmed
* PDUs can be sent synchronously and asynchronously.
*
* The Snmp
class is transport protocol independent. Support for
* a specific {@link TransportMapping} instance is added by calling the
* {@link #addTransportMapping(TransportMapping transportMapping)} method or
* creating a Snmp
instance by using the non-default constructor
* with the corresponding transport mapping. Transport mappings are used
* for incoming and outgoing messages.
*
* To setup a default SNMP session for UDP transport and with SNMPv3 support
* the following code snippet can be used:
*
*
* Address targetAddress = GenericAddress.parse("udp:127.0.0.1/161");
* TransportMapping transport = new DefaultUdpTransportMapping();
* snmp = new Snmp(transport);
* USM usm = new USM(SecurityProtocols.getInstance(),
* new OctetString(MPv3.createLocalEngineID()), 0);
* SecurityModels.getInstance().addSecurityModel(usm);
* transport.listen();
*
*
* How a synchronous SNMPv3 message with authentication and privacy is then
* sent illustrates the following code snippet:
*
*
* // add user to the USM
* snmp.getUSM().addUser(new OctetString("MD5DES"),
* new UsmUser(new OctetString("MD5DES"),
* AuthMD5.ID,
* new OctetString("MD5DESUserAuthPassword"),
* PrivDES.ID,
* new OctetString("MD5DESUserPrivPassword")));
* // create the target
* UserTarget target = new UserTarget();
* target.setAddress(targetAddress);
* target.setRetries(1);
* target.setTimeout(5000);
* target.setVersion(SnmpConstants.version3);
* target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
* target.setSecurityName(new OctetString("MD5DES"));
*
* // create the PDU
* PDU pdu = new ScopedPDU();
* pdu.add(new VariableBinding(new OID("1.3.6")));
* pdu.setType(PDU.GETNEXT);
*
* // send the PDU
* ResponseEvent response = snmp.send(pdu, target);
* // extract the response PDU (could be null if timed out)
* PDU responsePDU = response.getResponse();
* // extract the address used by the agent to send the response:
* Address peerAddress = response.getPeerAddress();
*
*
* An asynchronous SNMPv1 request is sent by the following code:
*
* // setting up target
* CommunityTarget target = new CommunityTarget();
* target.setCommunity(new OctetString("public"));
* target.setAddress(targetAddress);
* target.setRetries(2);
* target.setTimeout(1500);
* target.setVersion(SnmpConstants.version1);
* // creating PDU
* PDU pdu = new PDU();
* pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,1})));
* pdu.add(new VariableBinding(new OID(new int[] {1,3,6,1,2,1,1,2})));
* pdu.setType(PDU.GETNEXT);
* // sending request
* ResponseListener listener = new ResponseListener() {
* public void onResponse(ResponseEvent event) {
* // Always cancel async request when response has been received
* // otherwise a memory leak is created! Not canceling a request
* // immediately can be useful when sending a request to a broadcast
* // address.
* ((Snmp)event.getSource()).cancel(event.getRequest(), this);
* System.out.println("Received response PDU is: "+event.getResponse());
* }
* };
* snmp.sendPDU(pdu, target, null, listener);
*
*
* Traps (notifications) and other SNMP PDUs can be received by adding the
* folling code to the first code snippet above:
*
* CommandResponder trapPrinter = new CommandResponder() {
* public synchronized void processPdu(CommandResponderEvent e) {
* PDU command = e.getPDU();
* if (command != null) {
* System.out.println(command.toString());
* }
* }
* };
* snmp.addCommandResponder(trapPrinter);
*
*
*
* @author Frank Fock
* @version 1.10
*/
public class Snmp implements Session, CommandResponder {
private static final LogAdapter logger = LogFactory.getLogger(Snmp.class);
private static final int DEFAULT_MAX_REQUEST_STATUS = 2;
private static final int ENGINE_ID_DISCOVERY_MAX_REQUEST_STATUS = 0;
// Message processing implementation
private MessageDispatcher messageDispatcher;
/**
* The pendingRequests
table contains pending requests
* accessed trough the key PduHandle
*/
private Map pendingRequests = new Hashtable(50);
/**
* The asyncRequests
table contains pending requests
* accessed trough the key userObject
*/
private Map asyncRequests = new Hashtable(50);
// Timer for retrying pending requests
private CommonTimer timer;
// Listeners for request and trap PDUs
private transient Vector commandResponderListeners;
private TimeoutModel timeoutModel = new DefaultTimeoutModel();
// Dispatcher for notification listeners - not needed by default
private NotificationDispatcher notificationDispatcher = null;
// Default ReportHandler
private ReportHandler reportHandler = new ReportProcessor();
/**
* Creates a Snmp
instance that uses a
* MessageDispatcherImpl
with no message processing
* models and no security protols (by default). You will have to add
* those by calling the appropriate methods on
* {@link #getMessageDispatcher()}.
*
* At least one transport mapping has to be added before {@link #listen()}
* is called in order to be able to send and receive SNMP messages.
*
* To initialize a Snmp
instance created with this constructor
* follow this sample code:
*
* Transport transport = ...;
* Snmp snmp = new Snmp();
* SecurityProtocols.getInstance().addDefaultProtocols();
* MessageDispatcher disp = snmp.getMessageDispatcher();
* disp.addMessageProcessingModel(new MPv1());
* disp.addMessageProcessingModel(new MPv2c());
* snmp.addTransportMapping(transport);
* OctetString localEngineID = new OctetString(
* MPv3.createLocalEngineID());
* // For command generators, you may use the following code to avoid
* // engine ID clashes:
* // MPv3.createLocalEngineID(
* // new OctetString("MyUniqueID"+System.currentTimeMillis())));
* USM usm = new USM(SecurityProtocols.getInstance(), localEngineID, 0);
* disp.addMessageProcessingModel(new MPv3(usm));
* snmp.listen();
*
*/
public Snmp() {
this.messageDispatcher = new MessageDispatcherImpl();
}
/**
* Interface for handling reports.
*
* @author Frank Fock
* @version 1.6
* @since 1.6
*/
public static interface ReportHandler {
void processReport(PduHandle pduHandle, CommandResponderEvent event);
}
protected final void initMessageDispatcher() {
this.messageDispatcher.addCommandResponder(this);
this.messageDispatcher.addMessageProcessingModel(new MPv2c());
this.messageDispatcher.addMessageProcessingModel(new MPv1());
this.messageDispatcher.addMessageProcessingModel(new MPv3());
SecurityProtocols.getInstance().addDefaultProtocols();
}
/**
* Creates a Snmp
instance that uses a
* MessageDispatcherImpl
with all supported message processing
* models and the default security protols for dispatching.
*
* To initialize a Snmp
instance created with this constructor
* follow this sample code:
*
* Transport transport = ...;
* Snmp snmp = new Snmp(transport);
* OctetString localEngineID =
* new OctetString(snmp.getMPv3().getLocalEngineID());
* USM usm = new USM(SecurityProtocols.getInstance(), localEngineID, 0);
* SecurityModels.getInstance().addSecurityModel(usm);
* snmp.listen();
*
*
* @param transportMapping TransportMapping
* the initial TransportMapping
. You can add more or remove
* the same later.
*/
public Snmp(TransportMapping transportMapping) {
this();
initMessageDispatcher();
if (transportMapping != null) {
addTransportMapping(transportMapping);
}
}
/**
* Creates a Snmp
instance by supplying a
* MessageDispatcher
and a TransportMapping
.
*
* As of version 1.1, the supplied message dispatcher is not altered
* in terms of adding any message processing models to it. This has to be
* done now outside the Snmp class.
*
* To initialize a Snmp
instance created with this constructor
* follow this sample code:
*
* Transport transport = ...;
* SecurityProtocols.getInstance().addDefaultProtocols();
* MessageDispatcher disp = new MessageDispatcherImpl();
* disp.addMessageProcessingModel(new MPv1());
* disp.addMessageProcessingModel(new MPv2c());
* Snmp snmp = new Snmp(disp, transport);
* OctetString localEngineID = new OctetString(
* MPv3.createLocalEngineID());
* // For command generators, you may use the following code to avoid
* // engine ID clashes:
* // MPv3.createLocalEngineID(
* // new OctetString("MyUniqueID"+System.currentTimeMillis())));
* USM usm = new USM(SecurityProtocols.getInstance(), localEngineID, 0);
* disp.addMessageProcessingModel(new MPv3(usm));
* snmp.listen();
*
* @param messageDispatcher
* a MessageDispatcher
instance that will be used to
* dispatch incoming and outgoing messages.
* @param transportMapping
* the initial TransportMapping
,
* which may be null
. You can add or remove transport
* mappings later using {@link #addTransportMapping} and
* {@link #removeTransportMapping} respectively.
*/
public Snmp(MessageDispatcher messageDispatcher,
TransportMapping transportMapping) {
this.messageDispatcher = messageDispatcher;
this.messageDispatcher.addCommandResponder(this);
if (transportMapping != null) {
addTransportMapping(transportMapping);
}
}
/**
* Creates a Snmp
instance by supplying a
* MessageDispatcher
.
*
* The supplied message dispatcher is not altered
* in terms of adding any message processing models to it. This has to be
* done now outside the Snmp class.
*
*
* Do not forget to add at least one transport mapping before calling the
* listen method!
*
* To initialize a Snmp
instance created with this constructor
* follow this sample code:
*
* Transport transport = ...;
* SecurityProtocols.getInstance().addDefaultProtocols();
* MessageDispatcher disp = new MessageDispatcherImpl();
* disp.addMessageProcessingModel(new MPv1());
* disp.addMessageProcessingModel(new MPv2c());
* Snmp snmp = new Snmp(disp);
* snmp.addTransportMapping(transport);
* OctetString localEngineID = new OctetString(
* MPv3.createLocalEngineID());
* // For command generators, you may use the following code to avoid
* // engine ID clashes:
* // MPv3.createLocalEngineID(
* // new OctetString("MyUniqueID"+System.currentTimeMillis())));
* USM usm = new USM(SecurityProtocols.getInstance(), localEngineID, 0);
* disp.addMessageProcessingModel(new MPv3(usm));
* snmp.listen();
*
*
* @param messageDispatcher
* a MessageDispatcher
instance that will be used to
* dispatch incoming and outgoing messages.
* @since 1.5
*/
public Snmp(MessageDispatcher messageDispatcher) {
this.messageDispatcher = messageDispatcher;
this.messageDispatcher.addCommandResponder(this);
}
/**
* Returns the message dispatcher associated with this SNMP session.
* @return
* a MessageDispatcher
instance.
* @since 1.1
*/
public MessageDispatcher getMessageDispatcher() {
return messageDispatcher;
}
/**
* Adds a TransportMapping
to this SNMP session.
* @param transportMapping
* a TransportMapping
instance.
*/
public void addTransportMapping(TransportMapping transportMapping) {
// connect transport mapping with message dispatcher
messageDispatcher.addTransportMapping(transportMapping);
transportMapping.addTransportListener(messageDispatcher);
}
/**
* Removes the specified transport mapping from this SNMP session.
* If the transport mapping is not currently part of this SNMP session,
* this method will have no effect.
* @param transportMapping
* a previously added TransportMapping
.
*/
public void removeTransportMapping(TransportMapping transportMapping) {
messageDispatcher.removeTransportMapping(transportMapping);
transportMapping.removeTransportListener(messageDispatcher);
}
/**
* Adds a notification listener to this Snmp instance. Calling this method
* will create a transport mapping for the specified listening address and
* registers the provided CommandResponder
with the internal
* NotificationDispatcher
.
*
* @param listenAddress
* the Address
denoting the transport end-point
* (interface and port) to listen for incoming notifications.
* @param listener
* the CommandResponder
instance that should handle
* the received notifications.
* @return
* true
if registration was successful and false
* if, for example, the transport mapping for the listen address could not
* be created.
* @since 1.6
*/
public synchronized boolean addNotificationListener(Address listenAddress,
CommandResponder listener)
{
TransportMapping tm =
TransportMappings.getInstance().createTransportMapping(listenAddress);
if (tm == null) {
if (logger.isInfoEnabled()) {
logger.info("Failed to add notification listener for address: "+
listenAddress);
}
return false;
}
if (tm instanceof ConnectionOrientedTransportMapping) {
((ConnectionOrientedTransportMapping)tm).setConnectionTimeout(0);
}
tm.addTransportListener(messageDispatcher);
if (notificationDispatcher == null) {
notificationDispatcher = new NotificationDispatcher();
addCommandResponder(notificationDispatcher);
}
notificationDispatcher.addNotificationListener(listenAddress, tm, listener);
try {
tm.listen();
if (logger.isInfoEnabled()) {
logger.info("Added notification listener for address: "+
listenAddress);
}
return true;
}
catch (IOException ex) {
logger.warn("Failed to initialize notification listener for address '"+
listenAddress+"': "+ex.getMessage());
return false;
}
}
/**
* Removes (deletes) the notification listener for the specified transport
* endpoint.
* @param listenAddress
* the listen Address
to be removed.
* @return
* true
if the notification listener has been removed
* successfully.
*/
public synchronized boolean removeNotificationListener(Address listenAddress)
{
if (notificationDispatcher != null) {
if (logger.isInfoEnabled()) {
logger.info("Removing notification listener for address: "+
listenAddress);
}
return notificationDispatcher.removeNotificationListener(listenAddress);
}
else {
return false;
}
}
/**
* Puts all associated transport mappings into listen mode.
* @throws IOException
* if a transport mapping throws an IOException
when its
* {@link TransportMapping#listen()} method has been called.
*/
public void listen() throws IOException {
for (Iterator it = messageDispatcher.getTransportMappings().iterator();
it.hasNext(); ) {
TransportMapping tm = (TransportMapping) it.next();
if (!tm.isListening()) {
tm.listen();
}
}
}
/**
* Gets the next unique request ID. The returned ID is unique across
* the last 2^31-1 IDs generated by this message dispatcher.
* @return
* an integer value in the range 1..2^31-1. The returned ID can be used
* to map responses to requests send through this message dispatcher.
* @since 1.1
* @see MessageDispatcher#getNextRequestID
*/
public int getNextRequestID() {
return messageDispatcher.getNextRequestID();
}
/**
* Closes the session and frees any allocated resources, i.e. sockets and
* the internal thread for processing request timeouts.
*
* If there are any pending requests, the {@link ResponseListener} associated
* with the pending requests, will be called with a null
* response and a {@link InterruptedException} in the error member of the
* {@link ResponseEvent} returned.
*
* After a Session
has been closed it must not be used anymore.
* @throws IOException
* if a transport mapping cannot be closed successfully.
*/
public void close() throws IOException {
for (Iterator it = messageDispatcher.getTransportMappings().iterator();
it.hasNext(); ) {
TransportMapping tm = (TransportMapping) it.next();
if (tm.isListening()) {
tm.close();
}
}
CommonTimer t = timer;
timer = null;
if (t != null) {
t.cancel();
}
// close all notification listeners
if (notificationDispatcher != null) {
notificationDispatcher.closeAll();
}
List pr;
synchronized (pendingRequests) {
pr = new ArrayList(pendingRequests.values());
}
for (Iterator it = pr.iterator(); it.hasNext(); ) {
PendingRequest pending = (PendingRequest) it.next();
ResponseEvent e =
new ResponseEvent(this, null, pending.pdu, null, pending.userObject,
new InterruptedException(
"Snmp session has been closed"));
ResponseListener l = pending.listener;
if (l != null) {
l.onResponse(e);
}
}
}
/**
* Sends a GET request to a target. This method sets the PDU's type to
* {@link PDU#GET} and then sends a synchronous request to the supplied
* target.
* @param pdu
* a PDU
instance. For SNMPv3 messages, the supplied PDU
* instance has to be a ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public ResponseEvent get(PDU pdu, Target target) throws IOException {
pdu.setType(PDU.GET);
return send(pdu, target);
}
/**
* Asynchronously sends a GET request PDU
to the given target.
* The response is then returned by calling the supplied
* ResponseListener
instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public void get(PDU pdu, Target target, Object userHandle,
ResponseListener listener) throws IOException {
pdu.setType(PDU.GET);
send(pdu, target, userHandle, listener);
}
/**
* Sends a GETNEXT request to a target. This method sets the PDU's type to
* {@link PDU#GETNEXT} and then sends a synchronous request to the supplied
* target. This method is a convenience wrapper for the
* {@link #send(PDU pdu, Target target)} method.
* @param pdu
* a PDU
instance. For SNMPv3 messages, the supplied PDU
* instance has to be a ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public ResponseEvent getNext(PDU pdu, Target target) throws IOException {
pdu.setType(PDU.GETNEXT);
return send(pdu, target);
}
/**
* Asynchronously sends a GETNEXT request PDU
to the given
* target. The response is then returned by calling the supplied
* ResponseListener
instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public void getNext(PDU pdu, Target target, Object userHandle,
ResponseListener listener) throws IOException {
pdu.setType(PDU.GETNEXT);
send(pdu, target, userHandle, listener);
}
/**
* Sends a GETBULK request to a target. This method sets the PDU's type to
* {@link PDU#GETBULK} and then sends a synchronous request to the supplied
* target. This method is a convenience wrapper for the
* {@link #send(PDU pdu, Target target)} method.
* @param pdu
* a PDU
instance. For SNMPv3 messages, the supplied PDU
* instance has to be a ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public ResponseEvent getBulk(PDU pdu, Target target) throws IOException {
pdu.setType(PDU.GETBULK);
return send(pdu, target);
}
/**
* Asynchronously sends a GETBULK request PDU
to the given
* target. The response is then returned by calling the supplied
* ResponseListener
instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public void getBulk(PDU pdu, Target target, Object userHandle,
ResponseListener listener) throws IOException {
pdu.setType(PDU.GETBULK);
send(pdu, target, userHandle, listener);
}
/**
* Sends an INFORM request to a target. This method sets the PDU's type to
* {@link PDU#INFORM} and then sends a synchronous request to the supplied
* target. This method is a convenience wrapper for the
* {@link #send(PDU pdu, Target target)} method.
* @param pdu
* a PDU
instance. For SNMPv3 messages, the supplied PDU
* instance has to be a ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
.
* @throws IOException
* if the inform request could not be send to the specified target.
* @since 1.1
*/
public ResponseEvent inform(PDU pdu, Target target) throws IOException {
pdu.setType(PDU.INFORM);
return send(pdu, target);
}
/**
* Asynchronously sends an INFORM request PDU
to the given
* target. The response is then returned by calling the supplied
* ResponseListener
instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public void inform(PDU pdu, Target target, Object userHandle,
ResponseListener listener) throws IOException {
pdu.setType(PDU.INFORM);
send(pdu, target, userHandle, listener);
}
/**
* Sends a SNMPv1 trap to a target. This method sets the PDU's type to
* {@link PDU#V1TRAP} and then sends it to the supplied target. This method
* is a convenience wrapper for the {@link #send(PDU pdu, Target target)}
* method.
* @param pdu
* a PDUv1
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
. The selected SNMP protocol version for the
* target must be {@link SnmpConstants#version1}.
* @throws IOException
* if the trap cannot be sent.
* @since 1.1
*/
public void trap(PDUv1 pdu, Target target) throws IOException {
if (target.getVersion() != SnmpConstants.version1) {
throw new IllegalArgumentException(
"SNMPv1 trap PDU must be used with SNMPv1");
}
pdu.setType(PDU.V1TRAP);
send(pdu, target);
}
/**
* Sends a SNMPv2c or SNMPv3 notification to a target. This method sets the
* PDU's type to {@link PDU#NOTIFICATION} and then sends it to the supplied
* target. This method is a convenience wrapper for the
* {@link #send(PDU pdu, Target target)} method.
* @param pdu
* a PDUv1
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
. The selected SNMP protocol version for the
* target must be {@link SnmpConstants#version2c} or
* {@link SnmpConstants#version2c}.
* @throws IOException
* if the notification cannot be sent.
* @since 1.1
*/
public void notify(PDU pdu, Target target) throws IOException {
if (target.getVersion() == SnmpConstants.version1) {
throw new IllegalArgumentException(
"Notifications PDUs cannot be used with SNMPv1");
}
pdu.setType(PDU.NOTIFICATION);
send(pdu, target);
}
/**
* Sends a SET request to a target. This method sets the PDU's type to
* {@link PDU#SET} and then sends a synchronous request to the supplied
* target.
* @param pdu
* a PDU
instance. For SNMPv3 messages, the supplied PDU
* instance has to be a ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public ResponseEvent set(PDU pdu, Target target) throws IOException {
pdu.setType(PDU.SET);
return send(pdu, target);
}
/**
* Asynchronously sends a SET request PDU
to the given target.
* The response is then returned by calling the supplied
* ResponseListener
instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @throws IOException
* if the PDU cannot be sent to the target.
* @since 1.1
*/
public void set(PDU pdu, Target target, Object userHandle,
ResponseListener listener) throws IOException {
pdu.setType(PDU.SET);
send(pdu, target, userHandle, listener);
}
public ResponseEvent send(PDU pdu, Target target) throws IOException {
return send(pdu, target, null);
}
/**
* Sends a PDU
to the given target and if the PDU
* is a confirmed request, then the received response is returned
* synchronously.
* @param pdu
* a PDU
instance. When sending a SNMPv1 trap PDU, the
* supplied PDU instance must be a PDUv1
. For all types of
* SNMPv3 messages, the supplied PDU instance has to be a
* ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param transport
* specifies the TransportMapping
to be used when sending
* the PDU. If transport
is null
, the associated
* message dispatcher will try to determine the transport mapping by the
* target
's address.
* @return
* the received response encapsulated in a ResponseEvent
* instance. To obtain the received response PDU
call
* {@link ResponseEvent#getResponse()}. If the request timed out,
* that method will return null
. If the sent pdu
* is an unconfirmed PDU (notification, response, or report), then
* null
will be returned.
* @throws IOException
* if the message could not be sent.
* @see PDU
* @see ScopedPDU
* @see PDUv1
*/
public ResponseEvent send(PDU pdu, Target target,
TransportMapping transport) throws IOException {
return send(pdu, target, transport, DEFAULT_MAX_REQUEST_STATUS);
}
private ResponseEvent send(PDU pdu, Target target,
TransportMapping transport,
int maxRequestStatus) throws IOException {
if (!pdu.isConfirmedPdu()) {
sendMessage(pdu, target, transport, null);
return null;
}
if (timer == null) {
createPendingTimer();
}
SyncResponseListener syncResponse = new SyncResponseListener();
PendingRequest retryRequest = null;
synchronized (syncResponse) {
PduHandle handle = null;
PendingRequest request =
new PendingRequest(syncResponse, target, pdu, target, transport);
request.maxRequestStatus = maxRequestStatus;
handle = sendMessage(pdu, target, transport, request);
long totalTimeout =
timeoutModel.getRequestTimeout(target.getRetries(),
target.getTimeout());
long stopTime = System.currentTimeMillis()+totalTimeout;
try {
while ((syncResponse.getResponse() == null) &&
(System.currentTimeMillis() < stopTime)) {
syncResponse.wait(totalTimeout);
}
retryRequest = (PendingRequest) pendingRequests.remove(handle);
if (logger.isDebugEnabled()) {
logger.debug("Removed pending request with handle: " + handle);
}
request.setFinished();
request.cancel();
}
catch (InterruptedException iex) {
logger.warn(iex);
// cleanup request
request.setFinished();
request.cancel();
retryRequest = (PendingRequest) pendingRequests.remove(handle);
if (retryRequest != null) {
retryRequest.setFinished();
retryRequest.cancel();
}
Thread.currentThread().interrupt();
}
finally {
if (!request.finished) {
// free resources
retryRequest = (PendingRequest) pendingRequests.remove(handle);
if (retryRequest != null) {
retryRequest.setFinished();
retryRequest.cancel();
}
}
}
}
if (retryRequest != null) {
retryRequest.setFinished();
retryRequest.cancel();
}
if (syncResponse.getResponse() == null) {
syncResponse.response =
new ResponseEvent(Snmp.this, null, pdu, null, null);
}
return syncResponse.response;
}
private synchronized void createPendingTimer() {
if (timer == null) {
timer = SNMP4JSettings.getTimerFactory().createTimer();
}
}
public void send(PDU pdu, Target target,
Object userHandle,
ResponseListener listener) throws IOException {
send(pdu, target, null, userHandle, listener);
}
public void send(PDU pdu, Target target,
TransportMapping transport,
Object userHandle,
ResponseListener listener) throws IOException {
if (!pdu.isConfirmedPdu()) {
sendMessage(pdu, target, transport, null);
return;
}
if (timer == null) {
createPendingTimer();
}
PendingRequest request =
new AsyncPendingRequest(listener, userHandle, pdu, target, transport);
sendMessage(pdu, target, transport, request);
}
/**
* Sends a PDU
to the given target and returns the received
* response PDU
.
* @param pdu
* a PDU
instance. When sending a SNMPv1 trap PDU, the
* supplied PDU instance must be a PDUv1
. For all types of
* SNMPv3 messages, the supplied PDU instance has to be a
* ScopedPDU
instance.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @return
* the received response PDU
or null
* if the request timed out and if the PDU type of pdu
* is an unconfirmed PDU (i.e., trap or notification).
* @throws IOException
* if the message could not be sent.
* @see PDU
* @see ScopedPDU
* @see PDUv1
* @deprecated This method has been deprecated because it does not return
* the transport address of the entity (target) that sent the response.
* Please use {@link #send(PDU pdu, Target target)} instead. It returns
* a {@link ResponseEvent} object holding the response PDU and transport
* address of a successfully received response. This method will be supported
* until v2.0.
*/
public PDU sendPDU(PDU pdu, Target target) throws IOException {
ResponseEvent e = send(pdu, target);
if (e != null) {
return e.getResponse();
}
// pdu sent is unconfirmed one
return null;
}
/**
* Asynchronously sends a PDU
to the given target. The response
* is then returned by calling the supplied ResponseListener
* instance.
*
* @param pdu
* the PDU instance to send.
* @param target
* the Target instance representing the target SNMP engine where to send
* the pdu
.
* @param userHandle
* an user defined handle that is returned when the request is returned
* via the listener
object.
* @param listener
* a ResponseListener
instance that is called when
* pdu
is a confirmed PDU and the request is either answered
* or timed out.
* @deprecated Please use {@link #send(PDU pdu, Target target, Object
* userHandle, ResponseListener listener)} instead. It has exactly
* the same function but follows the new naming scheme. This method
* will be supported until v2.0.
* @throws IOException
* if the PDU could not be sent to the specified target.
*/
public void sendPDU(PDU pdu,
Target target,
Object userHandle,
ResponseListener listener) throws IOException {
send(pdu, target, userHandle, listener);
}
/**
* Actually sends a PDU to a target and returns a handle for the sent PDU.
* @param pdu
* the PDU
instance to be sent.
* @param target
* a Target
instance denoting the target SNMP entity.
* @param transport
* the (optional) transport mapping to be used to send the request.
* If transport
is null
a suitable transport
* mapping is determined from the target
address.
* @param pduHandleCallback
* callback for newly created PDU handles before the request is sent out.
* @throws IOException
* if the transport fails to send the PDU or the if the message cannot
* be BER encoded.
* @return PduHandle
* that uniquely identifies the sent PDU for further reference.
*/
protected PduHandle sendMessage(PDU pdu, Target target,
TransportMapping transport,
PduHandleCallback pduHandleCallback)
throws IOException
{
PduHandle handle = null;
if (target instanceof SecureTarget) {
SecureTarget secureTarget = (SecureTarget) target;
handle = messageDispatcher.sendPdu(transport,
secureTarget.getAddress(),
secureTarget.getVersion(),
secureTarget.getSecurityModel(),
secureTarget.getSecurityName().
getValue(),
secureTarget.getSecurityLevel(),
pdu, true, pduHandleCallback);
}
else if (target instanceof CommunityTarget) {
CommunityTarget communityTarget = (CommunityTarget) target;
int securityModel = SecurityModel.SECURITY_MODEL_SNMPv2c;
if (communityTarget.getVersion() == SnmpConstants.version1) {
securityModel = SecurityModel.SECURITY_MODEL_SNMPv1;
}
handle = messageDispatcher.sendPdu(transport,
communityTarget.getAddress(),
communityTarget.getVersion(),
securityModel,
communityTarget.getCommunity().
getValue(),
SecurityLevel.NOAUTH_NOPRIV,
pdu, true, pduHandleCallback);
}
return handle;
}
public void cancel(PDU request, ResponseListener listener) {
AsyncRequestKey key = new AsyncRequestKey(request, listener);
PduHandle pending = (PduHandle) asyncRequests.remove(key);
if (logger.isDebugEnabled()) {
logger.debug("Cancelling pending request with handle " + pending);
}
if (pending != null) {
PendingRequest pendingRequest =
(PendingRequest) pendingRequests.remove(pending);
if (pendingRequest != null) {
synchronized (pendingRequest) {
pendingRequest.setFinished();
pendingRequest.cancel();
}
}
}
}
/**
* Sets the local engine ID for the SNMP entity represented by this
* Snmp
instance. This is a convenience method that sets
* the local engine ID in the associated MPv3
and
* USM
.
* @param engineID
* a byte array containing the local engine ID. The length and content
* has to comply with the constraints defined in the SNMP-FRAMEWORK-MIB.
* @param engineBoots
* the number of boots of this SNMP engine (zero based).
* @param engineTime
* the number of seconds since the value of engineBoots last changed.
* @see MPv3
* @see USM
*/
public void setLocalEngine(byte[] engineID,
int engineBoots,
int engineTime) {
MPv3 mpv3 = getMPv3();
mpv3.setLocalEngineID(engineID);
USM usm = (USM) mpv3.getSecurityModel(SecurityModel.SECURITY_MODEL_USM);
usm.setLocalEngine(new OctetString(engineID), engineBoots, engineTime);
}
/**
* Gets the local engine ID if the MPv3 is available, otherwise a runtime
* exception is thrown.
* @return byte[]
* the local engine ID.
*/
public byte[] getLocalEngineID() {
return getMPv3().getLocalEngineID();
}
private MPv3 getMPv3() {
MPv3 mpv3 = (MPv3) getMessageProcessingModel(MessageProcessingModel.MPv3);
if (mpv3 == null) {
throw new NoSuchElementException("MPv3 not available");
}
return mpv3;
}
/**
* Discovers the engine ID of the SNMPv3 entity denoted by the supplied
* address. This method does not need to be called for normal operation,
* because SNMP4J automatically discovers authoritative engine IDs and
* also automatically synchronize engine time values.
*
* For this method to operate succesfully, the discover engine IDs
* flag in {@link USM} must be true
(which is the default).
*
* @param address
* an Address instance representing the transport address of the SNMPv3
* entity for which its authoritative engine ID should be discovered.
* @param timeout
* the maximum time in milliseconds to wait for a response.
* @return
* a byte array containing the authoritative engine ID or null
* if it could not be discovered.
* @see USM#setEngineDiscoveryEnabled(boolean enableEngineDiscovery)
*/
public byte[] discoverAuthoritativeEngineID(Address address, long timeout) {
MPv3 mpv3 = getMPv3();
// We need to remove the engine ID explicitly to be sure that it is updated
OctetString engineID = mpv3.removeEngineID(address);
// Now try to remove the engine as well
if (engineID != null) {
USM usm = getUSM();
if (usm != null) {
usm.removeEngineTime(engineID);
}
}
ScopedPDU scopedPDU = new ScopedPDU();
scopedPDU.setType(PDU.GET);
SecureTarget target = new UserTarget();
target.setTimeout(timeout);
target.setAddress(address);
target.setSecurityLevel(SecurityLevel.NOAUTH_NOPRIV);
try {
send(scopedPDU, target, null, ENGINE_ID_DISCOVERY_MAX_REQUEST_STATUS);
OctetString authoritativeEngineID = mpv3.getEngineID(address);
if (authoritativeEngineID == null) {
return null;
}
// we copy the byte array here, so we are sure nobody can modify the
// internal cache.
return new OctetString(authoritativeEngineID.getValue()).getValue();
}
catch (IOException ex) {
logger.error(
"IO error while trying to discover authoritative engine ID: " +
ex);
return null;
}
}
/**
* Gets the User Based Security Model (USM). This is a convenience method
* that uses the {@link MPv3#getSecurityModel} method of the associated MPv3
* instance to get the USM.
* @return
* the USM
instance associated with the MPv3 bound to this
* Snmp
instance, or null
otherwise.
*/
public USM getUSM() {
MPv3 mp = (MPv3) getMessageProcessingModel(MPv3.ID);
if (mp != null) {
return (USM)mp.getSecurityModel(SecurityModel.SECURITY_MODEL_USM);
}
return null;
}
/**
* Gets the message processing model for the supplied ID.
* @param messageProcessingModel
* a mesage processing model ID as defined in {@link MessageProcessingModel}.
* @return MessageProcessingModel
* a MessageProcessingModel
if
* messageProcessingModel
has been registered with the
* message dispatcher associated with this SNMP session.
*/
public MessageProcessingModel getMessageProcessingModel(int
messageProcessingModel) {
return messageDispatcher.getMessageProcessingModel(messageProcessingModel);
}
/**
* Process an incoming request or notification PDU.
*
* @param event
* a CommandResponderEvent
with the decoded incoming PDU as
* dispatched to this method call by the associated message dispatcher.
*/
public void processPdu(CommandResponderEvent event) {
PduHandle handle = event.getPduHandle();
PDU pdu = event.getPDU();
if (pdu.getType() == PDU.RESPONSE) {
event.setProcessed(true);
PendingRequest request;
if (logger.isDebugEnabled()) {
logger.debug("Looking up pending request with handle " + handle);
}
synchronized (pendingRequests) {
request = (PendingRequest) pendingRequests.get(handle);
if (request != null) {
request.responseReceived();
}
}
if (request == null) {
if (logger.isWarnEnabled()) {
logger.warn("Received response that cannot be matched to any " +
"outstanding request, address=" +
event.getPeerAddress() +
", requestID=" + pdu.getRequestID());
}
}
else {
ResponseListener l = request.listener;
if (l != null) {
l.onResponse(new ResponseEvent(this,
event.getPeerAddress(),
request.pdu,
pdu,
request.userObject));
}
}
}
else if (pdu.getType() == PDU.REPORT) {
event.setProcessed(true);
reportHandler.processReport(handle, event);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Fire process PDU event: " + event.toString());
}
fireProcessPdu(event);
}
}
class ReportProcessor implements ReportHandler {
public void processReport(PduHandle handle, CommandResponderEvent e) {
PDU pdu = e.getPDU();
logger.debug("Searching pending request with handle" + handle);
PendingRequest request = (PendingRequest) pendingRequests.get(handle);
if (request == null) {
logger.warn("Unmatched report PDU received from " + e.getPeerAddress());
return;
}
if (pdu.size() == 0) {
logger.error("Illegal report PDU received from " + e.getPeerAddress() +
" missing report variable binding");
return;
}
VariableBinding vb = pdu.get(0);
if (vb == null) {
logger.error("Received illegal REPORT PDU from " + e.getPeerAddress());
return;
}
OID firstOID = vb.getOid();
boolean resend = false;
if (request.requestStatus < request.maxRequestStatus) {
switch (request.requestStatus) {
case 0:
if (SnmpConstants.usmStatsUnknownEngineIDs.equals(firstOID)) {
resend = true;
}
else if (SnmpConstants.usmStatsNotInTimeWindows.equals(firstOID)) {
request.requestStatus++;
resend = true;
}
break;
case 1:
if (SnmpConstants.usmStatsNotInTimeWindows.equals(firstOID)) {
resend = true;
}
break;
}
}
// if legal report PDU received, then resend request
if (resend) {
logger.debug("Send new request after report.");
request.requestStatus++;
try {
// We need no callback here because we already have an equivalent
// handle registered.
PduHandle resentHandle =
sendMessage(request.pdu, request.target, e.getTransportMapping(),
null);
// make sure reference to handle is hold until request is finished,
// because otherwise cache information may get lost (WeakHashMap)
request.key = resentHandle;
}
catch (IOException iox) {
logger.error("Failed to send message to " + request.target + ": " +
iox.getMessage());
return;
}
}
else {
boolean intime;
// Get the request members needed before canceling the request
// which resets it
ResponseListener reqListener = request.listener;
PDU reqPDU = request.pdu;
Object reqUserObject = request.userObject;
synchronized (request) {
intime = request.cancel();
}
// remove pending request
// (sync is not needed as request is already canceled)
pendingRequests.remove(handle);
if (intime && (reqListener != null)) {
// return report
reqListener.onResponse(new ResponseEvent(this,
e.getPeerAddress(),
reqPDU,
pdu,
reqUserObject));
}
else {
// silently drop late report
if (logger.isInfoEnabled()) {
logger.info("Received late report from " +
e.getPeerAddress() +
" with request ID " + pdu.getRequestID());
}
}
}
}
}
/**
* Removes a CommandResponder
from this SNMP session.
* @param listener
* a previously added CommandResponder
instance.
*/
public synchronized void removeCommandResponder(CommandResponder listener) {
if (commandResponderListeners != null &&
commandResponderListeners.contains(listener)) {
Vector v = (Vector) commandResponderListeners.clone();
v.removeElement(listener);
commandResponderListeners = v;
}
}
/**
* Adds a CommandResponder
to this SNMP session.
* The command responder will then be informed about incoming SNMP PDUs of
* any kind that are not related to any outstanding requests of this SNMP
* session.
*
* @param listener
* the CommandResponder
instance to be added.
*/
public synchronized void addCommandResponder(CommandResponder listener) {
Vector v = (commandResponderListeners == null) ?
new Vector(2) : (Vector) commandResponderListeners.clone();
if (!v.contains(listener)) {
v.addElement(listener);
commandResponderListeners = v;
}
}
/**
* Fires a CommandResponderEvent
event to inform listeners about
* a received PDU. If a listener has marked the event as processed further
* listeners will not be informed about the event.
* @param event
* a CommandResponderEvent
.
*/
protected void fireProcessPdu(CommandResponderEvent event) {
if (commandResponderListeners != null) {
Vector listeners = commandResponderListeners;
int count = listeners.size();
for (int i = 0; i < count; i++) {
((CommandResponder) listeners.get(i)).processPdu(event);
// if event is marked as processed the event is not forwarded to
// remaining listeners
if (event.isProcessed()) {
return;
}
}
}
}
/**
* Gets the timeout model associated with this SNMP session.
* @return
* a TimeoutModel instance (never null
).
* @see #setTimeoutModel(TimeoutModel timeoutModel)
*/
public TimeoutModel getTimeoutModel() {
return timeoutModel;
}
/**
* Returns the report handler which is used internally to process reports
* received from command responders.
* @return
* the ReportHandler
instance.
* @since 1.6
*/
public ReportHandler getReportHandler() {
return reportHandler;
}
/**
* Sets the timeout model for this SNMP session. The default timeout model
* sends retries whenever the time specified by the timeout
* parameter of the target has elapsed without a response beeing received for
* the request. By specifying a different timeout model this behaviour can
* be changed.
* @param timeoutModel
* a TimeoutModel
instance (must not be null
).
*/
public void setTimeoutModel(TimeoutModel timeoutModel) {
if (timeoutModel == null) {
throw new NullPointerException("Timeout model cannot be null");
}
this.timeoutModel = timeoutModel;
}
/**
* Sets the report handler and overrides the default report handler.
* @param reportHandler
* a ReportHandler
instance which must not be
* null
.
* @since 1.6
*/
public void setReportHandler(ReportHandler reportHandler) {
if (reportHandler == null) {
throw new IllegalArgumentException("ReportHandler must not be null");
}
this.reportHandler = reportHandler;
}
class PendingRequest extends TimerTask implements PduHandleCallback {
private PduHandle key;
protected int retryCount;
protected ResponseListener listener;
protected Object userObject;
protected PDU pdu;
protected Target target;
protected TransportMapping transport;
private int requestStatus = 0;
// Maximum request status - allows to receive up to two reports and then
// send the original request again. A value of 0 is used for discovery.
private int maxRequestStatus = DEFAULT_MAX_REQUEST_STATUS;
private volatile boolean finished = false;
private volatile boolean responseReceived = false;
private volatile boolean pendingRetry = false;
private volatile boolean cancelled = false;
public PendingRequest(ResponseListener listener,
Object userObject,
PDU pdu,
Target target,
TransportMapping transport) {
this.userObject = userObject;
this.listener = listener;
this.retryCount = target.getRetries();
this.pdu = pdu;
this.target = (Target) target.clone();
this.transport = transport;
}
private PendingRequest(PendingRequest other) {
this.userObject = other.userObject;
this.listener = other.listener;
this.retryCount = other.retryCount - 1;
this.pdu = other.pdu;
this.target = other.target;
this.requestStatus = other.requestStatus;
this.responseReceived = other.responseReceived;
this.transport = other.transport;
}
protected void registerRequest(PduHandle handle) {
// overwritten by subclasses
}
public void responseReceived() {
this.responseReceived = true;
}
public synchronized void pduHandleAssigned(PduHandle handle, Object pdu) {
if (key == null) {
key = handle;
// get pointer to target before adding request to pending list
// to make sure that this retry is not being cancelled before we
// got the target pointer.
Target t = target;
if ((t != null) && (!cancelled)) {
pendingRequests.put(handle, this);
registerRequest(handle);
if (logger.isDebugEnabled()) {
logger.debug("Running pending " +
((listener instanceof SyncResponseListener) ?
"sync" : "async") +
" request with handle " + handle +
" and retry count left " + retryCount);
}
long delay =
timeoutModel.getRetryTimeout(t.getRetries() - retryCount,
t.getRetries(),
t.getTimeout());
if ((!finished) && (!responseReceived) && (!cancelled)) {
try {
timer.schedule(this, delay);
}
catch (IllegalStateException isex) {
// ignore
}
}
else {
pendingRequests.remove(handle);
}
}
}
}
/**
* Process retries of a pending request.
*/
public synchronized void run() {
PduHandle m_key = key;
PDU m_pdu = pdu;
Target m_target = target;
TransportMapping m_transport = transport;
ResponseListener m_listener = listener;
Object m_userObject = userObject;
if ((m_key == null) || (m_pdu == null) || (m_target == null) ||
(m_listener == null)) {
if (logger.isDebugEnabled()) {
logger.debug("PendingRequest canceled key="+m_key+", pdu="+m_pdu+
", target="+m_target+", transport="+m_transport+", listener="+
m_listener);
}
return;
}
try {
synchronized (pendingRequests) {
this.pendingRetry =
(!finished) && (retryCount > 0) && (!responseReceived);
}
if (this.pendingRetry) {
try {
PendingRequest nextRetry = new PendingRequest(this);
sendMessage(m_pdu, m_target, m_transport, nextRetry);
this.pendingRetry = false;
}
catch (IOException ex) {
ResponseListener l = listener;
finished = true;
logger.error("Failed to send SNMP message to " + m_target +
": " +
ex.getMessage());
messageDispatcher.releaseStateReference(m_target.getVersion(),
m_key);
if (l != null) {
listener.onResponse(new ResponseEvent(Snmp.this, null,
m_pdu, null, m_userObject, ex));
}
}
}
else if (!finished) {
finished = true;
pendingRequests.remove(m_key);
if (!cancelled) {
// request timed out
if (logger.isDebugEnabled()) {
logger.debug("Request timed out: " + m_key.getTransactionID());
}
messageDispatcher.releaseStateReference(m_target.getVersion(),
m_key);
m_listener.onResponse(new ResponseEvent(Snmp.this, null,
m_pdu, null, m_userObject));
}
}
else {
// make sure pending request is removed even if response listener
// failed to call Snmp.cancel
pendingRequests.remove(m_key);
}
}
catch (RuntimeException ex) {
logger.error("Failed to process pending request " + m_key +
" because " + ex.getMessage(), ex);
throw ex;
}
catch (Error er) {
logger.fatal("Failed to process pending request " + m_key +
" because " + er.getMessage(), er);
throw er;
}
}
public boolean setFinished() {
boolean currentState = finished;
this.finished = true;
return currentState;
}
public void setMaxRequestStatus(int maxRequestStatus) {
this.maxRequestStatus = maxRequestStatus;
}
public int getMaxRequestStatus() {
return maxRequestStatus;
}
public boolean isResponseReceived() {
return responseReceived;
}
/**
* Cancels the request and clears all internal fields by setting them
* to null
.
* @return
* true
if cancellation was successful.
*/
public boolean cancel(){
cancelled = true;
boolean result = super.cancel();
// free objects early
if (!pendingRetry) {
key = null;
pdu = null;
target = null;
transport = null;
listener = null;
userObject = null;
}
return result;
}
}
class AsyncPendingRequest extends PendingRequest {
public AsyncPendingRequest(ResponseListener listener,
Object userObject,
PDU pdu,
Target target,
TransportMapping transport) {
super(listener, userObject, pdu, target, transport);
}
protected void registerRequest(PduHandle handle) {
AsyncRequestKey key = new AsyncRequestKey(super.pdu, super.listener);
asyncRequests.put(key, handle);
}
}
static class AsyncRequestKey {
private PDU request;
private ResponseListener listener;
public AsyncRequestKey(PDU request, ResponseListener listener) {
this.request = request;
this.listener = listener;
}
/**
* Indicates whether some other object is "equal to" this one.
*
* @param obj the reference object with which to compare.
* @return true
if this object is the same as the obj argument;
* false
otherwise.
*/
public boolean equals(Object obj) {
if (obj instanceof AsyncRequestKey) {
AsyncRequestKey other = (AsyncRequestKey) obj;
return (request.equals(other.request) && listener.equals(other.listener));
}
return false;
}
public int hashCode() {
return request.hashCode();
}
}
static class SyncResponseListener implements ResponseListener {
private ResponseEvent response = null;
public synchronized void onResponse(ResponseEvent event) {
this.response = event;
this.notify();
}
public ResponseEvent getResponse() {
return response;
}
}
/**
* The NotificationDispatcher
dispatches traps, notifications,
* and to registered listeners.
*
* @author Frank Fock
* @version 1.6
* @since 1.6
*/
class NotificationDispatcher implements CommandResponder {
// A mapping of transport addresses to transport mappings of notification
// listeners
private Hashtable notificationListeners = new Hashtable(10);
private Hashtable notificationTransports = new Hashtable(10);
protected NotificationDispatcher() {
}
public synchronized void addNotificationListener(Address listenAddress,
TransportMapping transport,
CommandResponder listener){
notificationListeners.put(listenAddress, transport);
notificationTransports.put(transport, listener);
}
public synchronized boolean
removeNotificationListener(Address listenAddress)
{
TransportMapping tm =
(TransportMapping)notificationListeners.remove(listenAddress);
if (tm == null) {
return false;
}
tm.removeTransportListener(messageDispatcher);
notificationTransports.remove(tm);
try {
tm.close();
}
catch (IOException ex) {
logger.error(ex);
if (logger.isDebugEnabled()) {
ex.printStackTrace();
}
}
return true;
}
public synchronized void closeAll() {
notificationTransports.clear();
for (Iterator it = notificationListeners.values().iterator();
it.hasNext();) {
TransportMapping tm = (TransportMapping) it.next();
try {
tm.close();
}
catch (IOException ex) {
logger.error(ex);
if (logger.isDebugEnabled()) {
ex.printStackTrace();
}
}
}
notificationListeners.clear();
}
public synchronized void processPdu(CommandResponderEvent event) {
CommandResponder listener = (CommandResponder)
notificationTransports.get(event.getTransportMapping());
if ((event.getPDU() != null) &&
(event.getPDU().getType() == PDU.INFORM)) {
// try to send INFORM response
try {
sendInformResponse(event);
}
catch (MessageException mex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to send response on INFORM PDU event (" +
event + "): " + mex.getMessage());
}
}
}
if (listener != null) {
listener.processPdu(event);
}
}
/**
* Sends a RESPONSE PDU to the source address of a INFORM request.
* @param event
* the CommandResponderEvent
with the INFORM request.
* @throws
* MessageException if the response could not be created and sent.
*/
protected void sendInformResponse(CommandResponderEvent event) throws
MessageException {
PDU responsePDU = (PDU) event.getPDU().clone();
responsePDU.setType(PDU.RESPONSE);
responsePDU.setErrorStatus(PDU.noError);
responsePDU.setErrorIndex(0);
messageDispatcher.returnResponsePdu(event.getMessageProcessingModel(),
event.getSecurityModel(),
event.getSecurityName(),
event.getSecurityLevel(),
responsePDU,
event.getMaxSizeResponsePDU(),
event.getStateReference(),
new StatusInformation());
}
}
}