1

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?

Some Name
  • 8,555
  • 5
  • 27
  • 77

4 Answers4

1

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.

GClaramunt
  • 3,148
  • 1
  • 21
  • 35
0

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()
}
Hayk Hakobyan
  • 309
  • 2
  • 8
0

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
prayagupa
  • 30,204
  • 14
  • 155
  • 192
0

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.

Andy Hayden
  • 359,921
  • 101
  • 625
  • 535