Apache has IOUtils.closeQuietly(Closeable). In Scala, I'd like to generalize this: execute a block and ingore exceptions, while at the same time logging them. Something like this:
import LogUtils._
object Playground extends App {
implicit val logger_ = LoggerFactory.getLogger(getClass)
silentLog {
println("block")
throw new Exception("an exception")
}
println("end")
}
import org.slf4j.{Logger, LoggerFactory}
import scala.util.control.NonFatal
object LogUtils {
def silentLog[U](f: => U)(implicit log: Logger) {
try f
catch {
case NonFatal(e) => log.error(null, e)
}
}
}
Is this already implemented in some common library?
Try[T] does this to some extent but does not log the exception. Try does try .. catch ... inside
def LoggingTry[T](f: => T)(implicit logger: Logger): Try[T] = Try(f()).recoverWith {
case th =>
logger.log(th)
Try { throw th }
}
Use getOrElse(()) if you want to get value in case of success and default value (Unit) in case of failure
Related
I need to write a future that flushes a stream and closes it. Here is what I tried:
def close(ous: OutputStream) = Future.fromTry(Try {
try {
ous.flush()
} finally {
ous.close()
}
})
Looks ugly. try{}finally{} inside the Try. But I need finally block in order to avoid resource leak. Is there a way to rewrite the code in not so ugly way?
Future already captures exceptions returning a Future.failed, there's no need to do fromTry and block, so you can do:
Future { out.flush() }.andThen( _=> out.close )
( Future { out.flush() } will asynchronously flush the stream and andThen will get called wether it finishes or fails.
This could be an option i think.
def close(ous: OutputStream) = Future.fromTry(Try(ous.flush())) andThen {
case Success(_) => println("do something here")
case Failure(_) => ous.close()
}
It's not clear to me whether this is actually cleaner:
def close(ous: OutputStream) = Future.fromTry(
val flushed = Try { ous.flush() }
val closed = Try { ous.close() }
if (closed.isFailure) closed else flushed // bubble up the correct error
)
Note: This is nearly an equivalent of this answer but not quite. Mainly since the .close can fail, and this must be encapsulated in the Try.
since you are already using Try use pattern match on result to Try{ stream.flush } and apply Try{ stream.close() }
example,
import java.io.{ByteArrayOutputStream, OutputStream}
import java.util.Date
import java.io.ObjectOutputStream
import scala.concurrent.Future
import scala.util.Try
import scala.util.{Failure, Success}
import scala.concurrent.ExecutionContext.Implicits.global
def doSomeOperation: OutputStream => Future[String] = (outputStream: OutputStream) =>
withCleanup(outputStream) {
Future {
//1/0
outputStream.toString
}
}
def withCleanup(outputStream: OutputStream)(fn: Future[String]): Future[String] = {
val execution = fn
execution onComplete {
case Success(_) => cleanup(outputStream)
case Failure(_) => cleanup(outputStream)
}
execution
}
def cleanup(outputStream: OutputStream): Try[Unit] = Try {
outputStream.flush()
println("flushed")
} match {
case _ => Try {
outputStream.close()
println("closed")
}
}
Then call the function, which will flush and close your stream as well.
val stream = new ObjectOutputStream(new ByteArrayOutputStream())
stream.writeObject(new Date())
scala> doSomeOperation(stream)
res18: scala.concurrent.Future[String] = Future(<not completed>)
flushed
closed
I have a simple flow of orders from an controller to services back to the controller, and im trying to make sure I use future recovers in the right place and in general to cover exceptions properly.
controller action:
def getSiblings(): Action[JsValue] = Action.async(parse.json) { request =>
request.body.validate[Person] match {
case JsSuccess(person, _) =>
peopleService.getSiblings(person).map(res => Ok(Json.toJson(res))) recover {
case t: Throwable =>
logger.error("error running getSiblings: ", t)
InternalServerError
}
case JsError(errors) => Future(BadRequest(s"not a good person format ${errors.mkString}"))
}
}
peopleService:
class PeopleService #Inject() extends LazyLogging {
def getSiblings(personToGetSiblings: Person): Future[List[SiblingResults]] = {
// isSibling is a method of a person that returnes a future and can fail
Future.sequence(listOfPeople.map(person => person.isSibling(personToGetSiblings))) recover {
case e: Exception => {
throw new RuntimeException("fail to get siblings with error: ", e)
}
}
}
}
case class SiblingResults (person: Option[Person])
and a Person:
#Singleton
class PersonA #Inject() (configuration: Configuration, peopleApi: PeopleApi) extends Person {
def isSibling(personToMatch: Person): Future[SiblingResults] = {
val res = for {
// areSiblings returnes a Future[Boolean]
areThey <- peopleApi.areSiblings(personToMatch, personInstance) recover {
case ex: Exception => throw new Exception("PeopleApi failed")
}
} yield areThey
if (res) Some(personInstance) else None
}
val personInstance = this
...
}
what will be the right way to recover those futures?
Use Play's Action composition to handle any failures. This your code would be clean handling only the business logic without the extra plumbing stuff like exception handling etc. You let the exceptions bubble up to the controller and exception is handled finally by the ActionBuilder.
ActionBuilder
import play.api.libs.json.Json
import play.api.mvc.{ActionBuilder, Request, Result}
import play.api.mvc.Results.Status
import scala.concurrent.Future
/**
* Created by chlr on 12/2/16.
*/
object ErrRecoveryAction extends ActionBuilder[Request] {
def invokeBlock[A](request: Request[A], block: Request[A] => Future[Result]) = {
block(request) recover errorHandler
}
def errorHandler: PartialFunction[Throwable, Result] = {
// you can apply a different statuscode other than 500 depending on the type of exception matched.
case th: Throwable =>
new Status(500).apply(Json.obj("error_message" -> th.getMessage, "error_class" -> th.getClass.getName))
}
}
Controller usage:
Note how there is no exception handling in the controller and furthermore no exception handling is required in other service classes.
def getSiblings(): Action[JsValue] = ErrRecoveryAction.async(parse.json) {
request =>
peopleService
.getSiblings(request.body.as[Person])
.map(res => Ok(Json.toJson(res)))
}
In Slick 2.1 I had the code below to execute an sql-query from a file:
def fetchResult[T](sql: String)(implicit getResult: GetResult[T]): List[T] = {
val query = Q.queryNA[T](sql)
try {
Database.forDataSource(DB.getDataSource())
.withSession { implicit session => query.list }
}
catch {
case e: Throwable =>
throw new RunSqlException(s"Query $name execution error", e)
}
}
In Slick 3.0.0 you use dbConfig.db.run method to execute DBIOAction and get a future of the result. But I can't find a way to transform result of Q.queryNA (which is StaticQuery[Unit, R]) into DBIOAction. Does such a way exist?
I ended up with deprecated calls for now. Help me be better!
def fetchResult[T](sql: String)(implicit getResult: GetResult[T]): Future[List[T]] = Future {
val query = Q.queryNA[T](sql)
try {
this.dbConfig.db.withSession { implicit session => query.list }
}
catch {
case e: Throwable =>
throw new RunSqlException(s"Query $name execution error", e)
}
}
Only solution I managed to find was a bit hackish:
import slick.driver.HsqldbDriver.api._
def fetchResult[T](sql: String) = {
database.run(sqlu"""#$sql""")
}
In my current method, I am trying to make a series of calls and if any of them fail, I want to be able to continue running the remainder (while capturing the Exception that was thrown). I am having a hard time figuring this out in Scala.
So in this example, I want to kick off each of these calls - RunA, RunB and RunC but if RunB throws an exception, I want to print that and continue kicking off RunC after that.
var result = Try {
new RunA()
new RunB()
new RunC()
} catch {
case e: Throwable => e.printStackTrace()
false
}
Outside of having them all individually wrapped in a Try/Catch, I am sure there are better ways to do this which is why I am hoping someone can help with this.
I looked at the 'Ignoring' exception but it appears to completely ignore the exception which I want to atleast log.
Thanks!
First, don't mix try { ... } catch { ... } up with scala.util.Try{ ... }.
You can
import scala.util._
val runA = Try{ new RunA }
val runB = Try{ new RunB }
val runC = Try{ new RunC }
and then deal with the exceptions as you see fit. For instance, if you want to print and continue, you could deal with the try statements right there:
def getOrPrint[A](f: => A): Option[A] = Try{ f } match {
case Success(x) => Some(x)
case Failure(e) => e.printStackTrace; None
}
getOrPrint{ new RunA }
...
There can be more elegant ways for such things with scalaz (e.g. read an article here for some inspiration: http://johnkurkowski.com/posts/accumulating-multiple-failures-in-a-ValidationNEL/), but with "only" Scala you can do something like this:
import scala.reflect.ClassTag
import scala.util.{Try, Success, Failure}
def tryAndLog[T: ClassTag] = Try {
implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T] // new instance
} match {
case Success(_) => true
case Failure(ex) => ex.printStackTrace ; false
}
def tryRunAll = {
val A = tryAndLog[RunA]
val B = tryAndLog[RunB]
val C = tryAndLog[RunC]
A && B && C // returns true if all invocations succeeded, false otherwise
}
You are mixing scala.util.Try with try {} catch {} which are different concepts. Try wraps function into Success(result) or Failure(error) class, and try-catch is like Java try-catch. I suggest you something like this:
class RunA
class RunB
class RunC
class Result(a: RunA, b: RunB, c: RunC)
implicit class LogFailure[T](t: Try[T]) {
def logFailure: Try[T] = t match {
case scala.util.Failure(err) => err.printStackTrace(); t
case _ => t
}
}
val tryA= Try(new RunA())
val tryB= Try(new RunB())
val tryC = Try(new RunC())
val result: Try[Result] = for {
a <- tryA.logFailure
b <- tryB.logFailure
c <- tryC.logFailure
} yield {
// do smth with a, b, c
new Result(a, b, c)
}
If A, B, C will be successful you'll get Success(Result) if one of them failure you'll get Failure with first exception, however all of them will be logged (printed stack trace)
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.ScalaFutures
import org.apache.thrift.TApplicationException
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution {
it should "throw org.apache.thrift.TApplicationException for invalid Ids" in {
val future: Future[Response] = ThriftClient.thriftRequest
whenReady(future) {
res => {
intercept[TApplicationException] {
}
}
}
}
}
Question: How do you assert expected failures in Futures without blocking? The above doesn't work, the exception is thrown before the intercept block.
I know this is probably a bit late, but ScalaTest provides this feature out of the box (I believe since version 2) by mixing in the ScalaFutures trait, or using it directly in your test functions. Behold!
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f.failed) { e =>
e shouldBe a [SomeExceptionType]
}
}
Or you can perform some other assertions in there. Basically, if your future doesn't fail like you expect, the test will fail. If it fails, but throws a different exception, the test will fail. Nice and easy! =]
cheeky edit:
You can also use this method to test anything that returns a future:
test("some test") {
val f: Future[Something] = someObject.giveMeAFuture
ScalaFutures.whenReady(f) { s =>
// run assertions against the object returned in the future
}
}
Most recent edit!
I just wanted to update this answer with more useful information based on newer versions of Scala test. The various spec traits now all have async support, so instead of extending, say, WordSpec, you would instead extend AsyncWordSpec, and instead of relying on the whenReady calls as above, you would just map over your futures directly in the test.
Example:
class SomeSpec extends Async[*]Spec with Matchers {
...
test("some test") {
someObject.funcThatReturnsAFutureOfSomething map { something =>
// run assertions against the 'something' returned in the future
}
}
}
This was buried in a comment as well, but Scalatest's FutureValues mixin has you covered.
Just use f.failed.futureValue shouldBe an[TApplicationException]
Note: leaving this answer because the OP found it helpful, but for Scala Futures see the other answer.
This is a bit boilerplated, but Waiter from AsyncAssertions:
import org.scalatest.{ FlatSpec, Matchers, ParallelTestExecution }
import org.scalatest.concurrent.{ ScalaFutures, AsyncAssertions, PatienceConfiguration }
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
import util._
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[UnsupportedOperationException] {
w.await
}
}
}
given
import concurrent.Future
import concurrent.ExecutionContext.Implicits._
class Goof {
def goof(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new UnsupportedOperationException
}
def goofy(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
throw new NullPointerException
}
def foog(delay: Int = 1): Future[Int] = Future {
Thread sleep delay * 1000L
7
}
}
In other words,
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with AsyncAssertions {
it should "throw for invalid Ids" in {
val f: Future[Int] = new Goof().goof
import Helper._
f.failing[UnsupportedOperationException]
}
}
object Helper {
implicit class Failing[A](val f: Future[A]) extends Assertions with AsyncAssertions {
def failing[T <: Throwable](implicit m: Manifest[T]) = {
val w = new Waiter
f onComplete {
case Failure(e) => w(throw e); w.dismiss()
case Success(_) => w.dismiss()
}
intercept[T] {
w.await
}
}
}
}
Or, if you have multiple futures and you want the first non-conforming future to fail the test:
trait FailHelper extends Assertions with AsyncAssertions with PatienceConfiguration {
def failingWith[T <: Throwable : Manifest](fs: Future[_]*)(implicit p: PatienceConfig) {
val count = new java.util.concurrent.atomic.AtomicInteger(fs.size)
val w = new Waiter
for (f <- fs) f onComplete {
case Success(i) =>
w(intercept[T](i))
println(s"Bad success $i")
w.dismiss()
case Failure(e: T) =>
println(s"Failed $e OK, count ${count.get}")
w(intercept[T](throw e))
if (count.decrementAndGet == 0) w.dismiss()
case Failure(e) =>
println(s"Failed $e Bad")
w(intercept[T](throw e))
w.dismiss()
}
w.await()(p)
}
}
with usage
class Test extends FlatSpec with Matchers with ScalaFutures with ParallelTestExecution with FailHelper {
it should "throw for invalid Ids" in {
val sut = new Goof()
import sut._
val patienceConfig = null // shadow the implicit
implicit val p = PatienceConfig(timeout = 10 seconds)
// all should fail this way
//failingWith[UnsupportedOperationException](goof(), goofy(3), foog(5))
//failingWith[UnsupportedOperationException](goof(), foog(5))
failingWith[UnsupportedOperationException](goof(), goof(2), goof(3))
}
}
Inspired by this unloved answer.
ScalaTest 3.0 adds async versions of the spec traits like AsyncFreeSpec:
import org.scalatest.{AsyncFlatSpec, Matchers}
import scala.concurrent.Future
class ScratchSpec extends AsyncFlatSpec with Matchers {
def thriftRequest = Future { throw new Exception() }
it should "throw exception" in {
recoverToSucceededIf[Exception] {
thriftRequest
}
}
}
Addition to Brian Low's answer, I found a nice explanation for recoverToSucceededIf. This is available in all Async styles (from ScalaTest 3):
Failed futures can be tested in two ways: using recoverToSucceededIf or recoverToExceptionIf
recoverToSucceededIf is used for asserting the type of the exception the future ends in:
"return UserNotFoundException" when {
"the user does not exist" in {
recoverToSucceededIf[UserNotFoundException](userService.findUser("1"))
}
}
recoverToExceptionIf is useful when you want to test some of the exception's fields:
"return UserAlreadyExistsException" when {
"adding a user with existing username" in {
recoverToExceptionIf[UserAlreadyExistsException] {
userService.addUser(user)
}.map { ex =>
ex.message shouldBe s"User with username: $username already exists!"
}
}
}
See the whole blog from Tudor Zgureanu
—
What's new in ScalaTest 3
You can also try this Something Simple and Short
test("some test throwing SQL Exception") {
val f: Future[Something] = someObject.giveMeAFuture
recoverToSucceededIf[SQLException](f)
}