Tom Haigh
Tom Haigh

Reputation: 57815

How can I better match Java types in Scala?

I have a webservice that I am calling asynchronously from scala. I want to match and handle some specific fault codes that can be returned by the webservice in a recover block.

The error is returned as a SoapFault instance that is wrapped in an additional exception so essentially I need to match all exceptions where

I have this so far which is working but cumbersome:

call map { result => 
    // handle success
} recover {
    case e: Exception if e.getCause != null && e.getCause.isInstanceOf[SoapFault] && e.getCause.asInstanceOf[SoapFault].getMessage == "INVALID_INPUT" => {
        // handle error
    }
    case e: Exception if e.getCause != null && e.getCause.isInstanceOf[SoapFault] && e.getCause.asInstanceOf[SoapFault].getMessage == "SERVER_BUSY" => {
        // handle error
    }
}

How can I do this in a better way with less repetition?

Upvotes: 1

Views: 158

Answers (3)

Tom Haigh
Tom Haigh

Reputation: 57815

I have created my own extractor object that is similar to lmm's answer, but which extracts the error string (rather than checking it inside the extractor object) so that I can then match specific strings within the case expression.

Extractor:

 object SoapFaultMessage {
    def unapply(t: Throwable): Option[String] = {
      if (t.getCause != null && t.getCause.isInstanceOf[SoapFault]) {
        Some(t.getCause.asInstanceOf[SoapFault].getMessage)
      } else {
        None
      }
    }
  }

Usage:

 .. recover {
    case SoapFaultMessage(msg) if msg == "INVALID_INPUT" => {
      //handle
    }
 }

Upvotes: 0

4lex1v
4lex1v

Reputation: 21557

recover block handles errors that were thrown during (i.e inside) Future execution. As i understand from your question, although you are receiving a response which signals about some error, it doesn't throws any exception because the request itself was successful. It means that you need to handle your case inside with a flatMap, something like this:

val response = call flatMap {
  case SoapFault(error) => Future.failure(error)
  // successful response
}

In this case you your response would be a failure, so now you can handle your error with a recover block, but it's also not necessary, cause you can recover from your bad response inside flatMap function, cause it also returns a Future.

UPDATE

Like lmm proposed you can use extractor with the same name, i.e:

object SoapFault {
  def unapply(sf: SoapFault): Option[(Option[String], String)] = 
    Option((Option(sg.getCause), sf.getMessage))
}

Then deconstruct it in the block:

recover {
  case SoapFault(None, "INVALID_INPUT") => // process
}

Upvotes: 1

lmm
lmm

Reputation: 17431

You could create your own Extractor Object. Something like (untested):

object FaultWithMessage {
  def apply(message: String) = new Object() {
    def unapply(t: Throwable): Boolean = t match {
      case e: Exception if e.getCause != null &&
       e.getCause.isInstanceOf[SoapFault] &&
       e.getCause.asInstanceOf[SoapFault].getMessage == message => true
      case _ => false 
  }
}

... recover {
  case FaultWithMessage("INVALID_INPUT")() =>
    //handle that one
  case FaultWithMessage("SERVER_BUSY")() =>
    //handle that one
  ...
}

Upvotes: 4

Related Questions