0

I am trying to figure out how to use Scala Futures in a recursive manner. The use case I have is to compare the output of a resource ( ResourceOne) with another resource (ResourceTwo) output, if there are any discrepancies between them in the process I want to sync the first resource (ResourceOne) and retrieve the resource again till the number of items I retrieve from resourceOne are in sync with resourceTwo. But since my operations ( Retrieve and Remove ) with ResourceOne return Future, I am stuck half way through my logic. Below is a sample illustrating my problem, any thoughts on completing step #3 and #4 and making #1 thru #4 recursive for step #5 ?

package com.example

import scala.concurrent.Future
import scala.collection.mutable.Map
import scala.concurrent.ExecutionContext.Implicits.global

object ResourceOne {

  //var resources = List("one", "two","three","four","five","six","seven","eight","nine")
  var resources = List("one", "two","three")

  def getFirstFive():Future[List[String]] = Future {
    resources.take(5)
  }

  def remove(res:String) = Future {
    println(s"Deleting ${res}")
    resources = resources.filter( ! _.equals(res) )
  }
}

object ResourceTwo {

  val resourceDetails = Map("one" ->"oneDetail", "three" -> "threeDetail","four" ->"fourDetails","six" -> "sixDetail", "eight" -> "eightDetail")

  def getResourceDetail(resource:String) = {
    resourceDetails.get(resource);
  }
}

object HelloFuture {

  def main(args: Array[String]): Unit = {

    println("1. Fetching first five")
    val resF = ResourceOne.getFirstFive()

    println("2. Retrieving the resource detail from ResourceTwo, if a resourceDetail does not exist then delete the resource from resourceOne")
    val resFOut = resF.map( resList => {
        resList.map( resOne => {
            val resOneDetail = ResourceTwo.getResourceDetail( resOne )
            //println(s"Resource details for ${resOne} is ${resOneDetail}")
            if( resOneDetail == None) {
                println("2.a delete resource if detail is not available")
              ResourceOne.remove(resOne)
            }
          })
      })

    println("3. Verifying if all the resources retrieved from resourceOne were available in resourceTwo or not.")
    //TODO

    println("4. If all were not available then retrieve from resourceOne again as the missing ones will have been deleted.")
    //TODO

    println("5. Repeat step 1 thru 4 till all resources retreived from resourceOne are available in resourceTwo.")    

  }
}
mzafer
  • 791
  • 7
  • 21

1 Answers1

0

After looking around came up with the below solution. There is a minor issue with this one though, the ResourceOne.remove() returns a future and the implementation below does not handle it well and at causes the fetchResource() to run few times till the ResourcesOne.remove() complete. Not sure how it can be handled.

package com.example

import scala.concurrent.Future
import scala.util.{Success,Failure}
import scala.collection.mutable.Map
import scala.concurrent.ExecutionContext.Implicits.global

object ResourceOne {

  //var resources = List("one", "two","three","four","five","six","seven","eight","nine")
  var resources = List("one", "two","three")

  def getFirstFive():Future[List[String]] = Future {
    resources.take(5)
  }

  def remove(res:String) = Future {
    println(s"Deleting ${res}")
    resources = resources.filter( ! _.equals(res) )
  }
}

object ResourceTwo {

  val resourceDetails = Map("one" ->"oneDetail", "three" -> "threeDetail","four" ->"fourDetails","six" -> "sixDetail", "eight" -> "eightDetail")

  def getResourceDetail(resource:String) = {
    resourceDetails.get(resource);
  }
}

object HelloFuture {

  def main(args: Array[String]): Unit = {

    val fOut = fetchResource()
    fOut.map{ detail => println("Final Detail retreived is "+ detail) }

  }

  // Gets from resourceOne and if the detail is not found in resourceTwo then deletes from resourceOne and refetches from resourceOne till both the fetches match
  def fetchResource():Future[List[Any]] = {
    println("Fetching first five")
    val resF = ResourceOne.getFirstFive()

    var resOneCount = 0;
    val resFOut = resF.map( resList => {
        resList.map( resOne => {
            val resOneDetail = ResourceTwo.getResourceDetail( resOne )
            resOneCount += 1
            resOneDetail match {
              case Some(_) => { resOneDetail }
              case None =>  { ResourceOne.remove(resOne) ; resOneDetail }
            }
          })
      })

    resFOut flatMap {
      case  x if x.filter(_ != None).size == resOneCount => Future.successful(x)
      case _ => {  fetchResource() }
    }
  }

}
mzafer
  • 791
  • 7
  • 21