I am querying SNMP and I want to execute a callback when the data retrieval is complete. For this I am using the following code:
def treeWalk(rootOid: String)(callback: (Seq[Tuple2[OID, Variable]]) => Unit) {//{{{
val transport = new DefaultUdpTransportMapping
val snmp = new Snmp(transport)
transport.listen
val target = new CommunityTarget
target.setCommunity(new OctetString(community))
target.setAddress(GenericAddress.parse("udp:" + address + "/161"))
target.setRetries(retries)
target.setTimeout(timeout)
target.setVersion(SnmpConstants.version2c)
val treeUtil = new TreeUtils(snmp, new DefaultPDUFactory)
var variables: Seq[VariableBinding] = List()
var _isFinished = false
val listener = new TreeListener {
def next(event: TreeEvent) = {
println("NEXT")
val vars = event.getVariableBindings
if(vars != null)
variables ++= vars
true
}
def isFinished() = _isFinished
def finished(event: TreeEvent) {
val vars = event.getVariableBindings
println("DONE")
if(vars != null)
variables ++= vars
_isFinished = true
snmp.close
callback(variables.map(v => (v.getOid, v.getVariable)))
}
}
treeUtil.walk(target, Array(new OID(rootOid)), null, listener)
}
I get no errors or exceptions, however I never see the print lines in next() and finished() and also the callback is not executed. What am I doing wrong, how should I be hooking the event listener to it?
PS: The one line syncronous version returns data just fine...
I am using Scala 2.11.6 and SNMP4J 2.3
When dealing with callbacks, you have to take into account that the main thread can easily complete before your asynchronous call, causing termination of the main thread (and by extension the rest of the program) before the asynchronous task is completed and the callback reached. The simple way to do this is to invoke Thread.sleep()
and make your main thread pause for a specific amount of time to wait. However, you can improve on this. One option is to have a snippet of code like this:
var callbackComplete = false
someAsyncMethodWithCallback(callback)
while (!callbackComplete){
Thread.sleep(100)
}
Then all you need to do is have your callback include a line saying callbackComplete = true
and this will make it terminate.
However, this is far from ideal (but fine if you just need a quick temporary fix). Ideally, you should read through the Futures and Promises page in the Scala documentation. It details how to handle Futures properly, as well as deal with failures, and should be considered the proper resource for dealing with asynchronous calls.