Scala bindings for BOSSWAVE. Fully compatible with the Hadron (2.4.x) release.
val client = new BosswaveClient()
client.overrideAutoChainTo(true)
client.setEntityFile("devKey.ent")
val po = new PayloadObject(Some(POAllocations.PODFString), content = "Hello, World!")
client.publish("scratch.ns/test", payloadObjects = Seq(po))val client = new BosswaveClient()
client.overrideAutoChainTo(true)
client.setEntityFile("devKey.ent")
client.subscribe("scratch.ns/test", { result =>
result.payloadObjects.find(_.octet.contains(POAllocations.PODFString)).foreach(println(_.content))
})The second argument is a closure that takes a BosswaveResult instance as its
only parameter.
All BosswaveClient methods have a return type of Future[BosswaveResponse],
with the exception of the queryAll and listAll methods. This gives the
caller the ability to make each Bosswave operation either synchronous or
asynchronous.
Because they involve the instantiation of Futures, the Bosswave client methods
all require an ExecutionContext as an implicit parameter. This gives the user
the ability to specify the resources underlying the asynchronous execution of
Bosswave operations, such as a traditional thread pool. If you don't know what
any of this means, just include the following import statement in your code,
which provides a sensible default:
import scala.concurrent.ExecutionContext.Implicits.globalThe two simple examples above demonstrate the "fire and forget" approach. The client performs the operation asynchronously, and the caller does not perform any error checking.
To perform a Bosswave operation asynchronously while still checking the outcome,
one can use Scala's onComplete method. For example:
client.publish("scratch.ns/test", payloadObjects = ...).onComplete {
case Failure(cause) =>
println("Publish failed with exception: " + cause)
case Success(BossaveResponse(status, reason)) =>
if (status != "okay") {
println("Bosswave operation failed: " + reason.get)
}
}Note that this does not use Future's onFailure method, as any valid Bosswave
response is returned within a Success object, even if it reflects an error.
Instead, Failure is reserved for the case where an exception occurs.
As this is a common use case, BosswaveClient provides a utility function
to eliminate boilerplate code. This does involve (ab)use of implicits, so a
special import is required. The previous example can be written as:
import edu.berkeley.cs.sdb.bw2.BosswaveClient.BosswaveFuture
...
client.publish("scratch.ns/test", payloadObjects = ...).exitIfError()To perform a blocking operation and directly obtain the relevant
BosswaveResponse, one can use Scala's Await utility. For
example, to block until a publish is finished and check for errors:
val resp = Await.result(client.publish("scratch.ns/test", payloadObjects = ...), Duration.Inf)
if (resp.status != "okay") {
println("Bosswave publish failed: " + resp.reason.get)
}The BosswaveClient includes a utilty to avoid boilerplate code:
import edu.berkeley.cs.sdb.bw2.BosswaveClient.BosswaveFuture
...
val resp = client.publish("scratch.ns/test", payloadObjects = ...).await()
...By default, await takes no arguments and blocks indefinitely until the
corresponding Bosswave operation has finished. One can also provide a Scala
Duration instance as an argument to specify a timeout interval.
The Bosswave Go bindings provide orExit variants on most operations to
simplify error handling. The Scala bindings feature a similar primitive that
blocks until an operation has completed and exits the program if an error has
occurred. Otherwise, execution proceeds normally. For example:
import edu.berkeley.cs.sdb.bw2.BosswaveClient.BosswaveFuture
...
client.publish("scratch.ns/test", payloadObjects = ...).completeOrExit()