When using some code like the following:
scala> Future { null } onComplete { case Success(v) => v.toString }
Scala throws the following exception:
scala> java.lang.NullPointerException
at $line14.$read$$iw$$iw$$anonfun$2.apply(<console>:11)
at $line14.$read$$iw$$iw$$anonfun$2.apply(<console>:11)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
This would be OK, since I am not handling any exceptions. The problem is that my application hangs completely.
I am using concurrent.ExecutionContext.Implicits.global and I think onComplete is executed in this global execution context. The problem is that it seems like the execution context stops accepting any work and and the application just hangs.
Do I have to explicitly use try ... catch so that I protect my app in case something unexpected happens in onComplete?
Thank you
IIRC, this was an issue only in the very earliest implementation.
You can supply a handler or "reporter":
scala> import util._
import util._
scala> import concurrent._
import concurrent._
scala> ExecutionContext.fromExecutor(null, (t: Throwable) => println(s"Hi, $t"))
res0: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl#221a3fa4
scala> implicit val x = res0
x: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl#221a3fa4
scala> Future { null } onComplete { case Success(v) => v.toString }
<console>:16: warning: match may not be exhaustive.
It would fail on the following input: Failure(_)
Future { null } onComplete { case Success(v) => v.toString }
^
Hi, java.lang.NullPointerException
scala>
Everything is handled.
First of all, the NullPointerException you get has nothing to do with the future; it does not happen inside the Future block.
What you can do, is wrapping code that might return null in Option().
Your code would then look like this:
Future { Option(mightBeANull) } onComplete { case Success(v) => v.map(_.toString) }
Related
I am playing around with Future.recover (through a scala sheet in intelJ if it has any importance)
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def get():Future[Int] = {
throw new ClassCastException
}
val n = get().map{
x => x + 1
}.recover{
case e: Throwable => print("we recovered")
0
}
n.map(print(_)) // not getting here
I was expecting 0 to be printed. However, this what I am getting:
java.lang.ClassCastException
at #worksheet#.get(test.sc:5)
at #worksheet#.n$lzycompute(test.sc:8)
at #worksheet#.n(test.sc:8)
at #worksheet#.get$$instance$$n(test.sc:8)
at A$A76$.main(test.sc:32)
at #worksheet#.#worksheet#(test.sc)
Why is my recover not working. am I using it incorrectly ?
Your get function doesn't return a Future. It just immediately throws the ClassCastException. You need to create the Future somewhere.
Change your get function to this:
def get(): Future[Int] = Future {
throw new ClassCastException
}
You probably want something like:
Future.failed(new ClassCastException)
The signature is:
def failed[T](exception: Throwable): scala.concurrent.Future[T]
I am using monix tasks and i am trying to catch a Throwable and then convert to a custom error. I have removed/changed the code to be simple and relevant. This is the code (question follows after the code snippet):
import io.netty.handler.codec.http.HttpRequest
import monix.reactive.Observable
import io.netty.buffer.ByteBuf
import monix.eval.Task
import com.mypackage.Response
private[this] def handler(
request: HttpRequest,
body: Observable[ByteBuf]
): Task[Response] = {
val localPackage = for {
failfast <- Task.eval(1 / 0)
} yield failfast
// Failure case.
localPackage.onErrorRecoverWith {
case ex: ArithmeticException =>
print(s"LOG HERE^^^^^^^^^^^^^^^")
return Task.now(
Response(HttpResponseStatus.BAD_REQUEST,
None,
None)
)
}.runAsync
// Success case.
localPackage.map { x =>
x match {
case Right(cool) =>
Response(
HttpResponseStatus.OK,
None,
cool
)
case Left(doesntmatter) => ???
}
}
}
I am able to see the print statement but the expected Task.now(Response(... is not being returned. Instead the method that calls the handler method is throwing an error. How do i make it return the Task[Response] ?
The success case works, the failure case does not.
Edit #1 : Fix errors in scala code.
Edit #2 This is how I fixed it.
// Success case.
localPackage.map { x =>
x match {
case Right(cool) =>
Response(
HttpResponseStatus.OK,
None,
cool
)
case Left(doesntmatter) => ???
}
}.onErrorRecoverWith {
case ex: ArithmeticException =>
print(s"LOG HERE^^^^^^^^^^^^^^^")
return Task.now(
Response(HttpResponseStatus.BAD_REQUEST,
None,
None)
)
}
I was thinking in terms of future and forgot the lazy eval nature of task. Also I understood how the CancellableFuture value was being discarded in the failure task.
Several problems with your sample.
For one this code isn't valid Scala:
val localPackage = for {
failfast <- 1 / 0
} yield failfast
I guess you meant Task.eval(1 / 0).
Also onErrorHandle does not have a Task as a return type, you were probably thinking of onErrorHandleWith. And it's a pretty bad idea to give it a partial function (i.e. a function that can throw exceptions due to matching errors) — if you want to match on that error, then better alternatives are onErrorRecover and onErrorRecoverWith, which take partial functions as arguments.
So here's a sample:
import monix.eval._
import monix.execution.Scheduler.Implicits.global
val task = Task.eval(1 / 0).onErrorRecoverWith {
case _: ArithmeticException => Task.now(Int.MinValue)
}
task.runAsync.foreach(println)
//=> -2147483648
Hope this helps.
Is it possible to pattern match on a lazy val, declared as a Try, like this?
lazy val kafkaProducer: Try[producer.KafkaProducer[Array[Byte], String]] = Try(kafkaProducerSettings.createKafkaProducer())
...
kafkaProducer.get match {
case Success(_) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer.get))
case Failure(x) => Future.failed(x)
}
I'm getting this error:
constructor cannot be instantiated to expected type;
[error] found : akka.actor.Status.Success
[error] required: org.apache.kafka.clients.producer.KafkaProducer[Array[Byte],String]
[error] case Success(_) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer.get))
Note, this alternative code works, but I'm not sure it's the "Scala way":
lazy val kafkaProducer: producer.KafkaProducer[Array[Byte], String] = kafkaProducerSettings.createKafkaProducer()
...
val tryAccessLazyKafkaProducer = Try(kafkaProducer)
if (tryAccessLazyKafkaProducer.isSuccess) {
Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, kafkaProducer))
} else {
Future.failed(tryAccessLazyKafkaProducer.failed.get)
}
It's definitely possible, you just have the wrong Success type imported:
found : akka.actor.Status.Success
You need scala.util.Success instead
One thing you mustn't do is call Try.get, which will explode if the returned type is a Failure. Instead, do:
import scala.util.Success
import scala.util.Failure
kafkaProducer match {
case Success(producer) => Source.single(producerRecord()).runWith(Producer.plainSink(kafkaProducerSettings, producer))
case failure: Failure => failure
}
lazy is just a language construct which makes sure the value is only ever evaluated once. The underlying type, whether lazy or not, is still a Try which you can do what you do with it.
I have a method that returns Future[Try[Option[Int]]]. I want to extract value of Int for further computation. Any idea how to process it??
future.map(_.map(_.map(i => doSomethingWith(i))))
If you want use cats you can do fun (for certain definitions of fun) things like:
import scala.concurrent._
import scala.util._
import scala.concurrent.ExecutionContext.Implicits.global
import cats.Functor
import cats.instances.option._
import cats.implicits._
val x = Future { Try { Some(1) } } // your type
Functor[Future].compose[Try].compose[Option].map(x)(_ + 2)
This is suggested ONLY if you're already familiar with cats or scalaz.
Otherwise, you're great to go with any of the other valid answers here (I especially like the map-map-map one).
Just map the future and use match case to handle the different cases:
val result: Future[Try[Option[Int]]] = ???
result.map {
case Success(Some(r)) =>
println(s"Success. Result: $r")
//Further computation here
case Success(None) => //Success with None
case Failure(ex) => //Failed Try
}
Converting Future[Try[Option[Int]]] to Future[Int]
One hacky way is to convert the unfavourable results into failed future and flatMapping over.
Convert try failures to Future failures preserving the information that exception originated from Try and convert None to NoneFound exception.
val f: Future[Try[Option[Int]]] = ???
case class TryException(ex: Throwable) extends Exception(ex.getMessage)
case object NoneFound extends Exception("None found")
val result: Future[Int] = f.flatMap {
case Success(Some(value)) => Future.successful(value)
case Success(None) => Future.failed(NoneFound)
case Failure(th) => Future.failed(TryException(th))
}
result.map { extractedValue =>
processTheExtractedValue(extractedValue)
}.recover {
case NoneFound => "None case"
case TryException(th) => "try failures"
case th => "future failures"
}
Now in every case you know from where the exception has originated. In case of NoneFound exception you know Future and Try are successful but option is none. This way information is not lost and nested structure is flattened to Future[Int].
Now result type would be Future[Int]. Just use map, flatMap, recover and recoverWith to compose further actions.
If you really concerned about extraction see this, else go through the answer by #pamu to see how you actually use your Future.
Suppose your Future value is result.
Await.ready(result, 10.seconds).value.get.map { i => i.get}.get
Obviously this wont get through your failure and None cases and would throw exceptions and Await is not recommended.
So if you want to handle Failure and None case ->
val extractedValue = Await.ready(f, 10.seconds).value.get match {
case Success(i) => i match {
case Some(value) => value
case None => println("Handling None here")
}
case Failure(i) => println("Handling Failure here")
}
I have a Set of Future[T] that I want to manage into a single object for a library I am writing. In my current implementation I'm using a Future.sequence to collect them all and wait until they've resolved so I can do futurey things on them (map, collect, filter). However this only gives me the ability to match on Success or Failure, which is not necessarily the case for the collection of futures I'm dealing with. Some will fail and some will succeed and I would like to be able to extract the values I can from those that succeed and collect the exceptions and errors on the others so that I can escalate them appropriately. In pseudo code it would be something like
Future.sequence(Set[Future[T]]) andThen {
case FullSuccess => "woot"
case SomeErrors => "well, that's still ok."
case FullErrors => "Ok, who's the wise guy."
}
What I'm really looking for is to have data where there is data and not have to return a complete failure if only 1 of the futures in the sequence has failed.
Thanks for the help.
Unfortunately there is no builtin helper for your case, but it's easy to create your own:
import scala.concurrent.{Await, Future}
import scala.util.{Failure, Success, Try}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.DurationInt
def sequenceOfTries[T](futures: Seq[Future[T]]): Future[Seq[Try[T]]] =
futures.foldLeft(Future.successful(List[Try[T]]())) {
case (accF, f) => accF.flatMap {
acc => f.map(v => Success(v) :: acc).recover { case ex => Failure(ex) :: acc }
}
}.map(_.reverse)
val v = Seq(
Future.successful(1),
Future.failed(new IllegalStateException("2")),
Future.successful(3),
Future.failed(new IllegalStateException("4"))
)
Await.result(sequenceOfTries(v), 1.second)
Results:
v: Seq[scala.concurrent.Future[Int]] = List(scala.concurrent.impl.Promise$KeptPromise#2416f7e5, scala.concurrent.impl.Promise$KeptPromise#2aaf675d, scala.concurrent.impl.Promise$KeptPromise#360d48f, scala.concurrent.impl.Promise$KeptPromise#230f8be2)
res0: Seq[scala.util.Try[Int]] = List(Success(1), Failure(java.lang.IllegalStateException: 2), Success(3), Failure(java.lang.IllegalStateException: 4))
UPD. Alternatively you can utilize Future.sequence like this (with same result):
def sequenceOfTries[T](futures: Seq[Future[T]]): Future[Seq[Try[T]]] =
Future.sequence(futures.map(_.map(x => Success(x)).recover { case ex => Failure(ex) }))