I'm using Spring Boot 2.0.0.M7 and Project Reactor. My issue is relating to some strange behavior encountered while writing a Unit test. I also ran into this while trying to feed the output of flatMap into the repository.
Mono<Foo> create(Mono<FooResource> resourceMono) {
resourceMono.flatMap({
// convert resource into Foo domain Entity
return new Foo()
})
}
This closure should emit a Mono<Foo> due to the return value of flatMap. However, when calling block() to subscribe and get the resulting Foo, there is a ClassCastException in FluxFlatMap.trySubscribeScalarMap
Test code:
def createdFoo = Foo.create(Mono.just(fooResource)).block()
Stack Trace:
java.lang.ClassCastException: com.example.Foo cannot be cast to org.reactivestreams.Publisher
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:141)
at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:53)
at reactor.core.publisher.Mono.block(Mono.java:1161)
This appears to occur because MonoJust implements Callable, so trySubscribeScalarMap tries to unwrap it unsuccessfully.
In a non-test case scenario, a similar error occurs.
Mono<ServerResponse> createFoo(ServerRequest request) {
def body = request.body(BodyExtractors.toMono(FooResource))
ok().body(fromPublisher(Foo.create(body)
.flatMap({fooRepository.save(it)}), Foo))
}
Stack trace:
java.lang.ClassCastException: com.example.Foo cannot be cast to reactor.core.publisher.Mono
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:118) [reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:450) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1092) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at reactor.core.publisher.MonoSingle$SingleSubscriber.onComplete(MonoSingle.java:171) ~[reactor-core-3.1.2.RELEASE.jar:3.1.2.RELEASE]
<...>
Assembly trace from producer [reactor.core.publisher.MonoFlatMap] :
reactor.core.publisher.Mono.flatMap(Mono.java:2059)
org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrap.invoke(PojoMetaMethodSite.java:213)
org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
com.example.Foo.create(Foo.groovy:28)
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
com.example.FooHandlerFunctions.createFoo(FooHandlerFunctions.groovy:48)
Wrapping the output of the flatMap closure in another Mono.just(foo) solves both issues. However, it seems like that shouldn't be needed. Am I doing something wrong or just misunderstanding how flatMap works here?
the flatmap takes a Function that should return a Mono (I guess groovy let you return the wrong type, as Foo doesn't seem to implement Publisher?)
Related
I am using Scala with Play for my microservice. It has a Controller that uses the Action.async construct with a custom body parser. Here's a sample code:
def crud(param: String) = Action.async(SomeCustomBodyParser) { implicit request =>
try {
<some code>
} catch {
case _ => <Exception Handling logic>
}
}
The issue with this code is that in case of an exception in SomeCustomBodyParser, it does not get handled in the catch block. I tried a few approaches where I extract it outside and then handle it manually, but the exception is not caught correctly. The code of Action.async suggests that it takes a block of code and executes it a separate context. I am unclear exactly how it works.
How can I handle the exception and spit out a better exception message.
The Action.async has to be given a Future[Result], which can be completed either with a successful Result or a failure.
Any failed Future there results in an error HTTP response.
Action.async { Future.failed(new Exception("Foo") }
The way such error is formatted can be cistomized.
The problem here is that you are trying to use synchronous error handler to handle asynchronous error. try catch can only handle synchronous errors. Future[_] is asynchronous by nature and it will, if any, throw error after your try catch statement has already been executed (and possibly in a different thread).
Instead, in scala, we make error handling explicit by using a data structure like Option or Either or \/ from scalaz. All these wrappers form Monad.
In most asynchronous server setting, what you want is Future with Either inside (or right-biased variant like \/ from scalaz.) This way, you abstract over both asynchronity and error handling. Since both wrappers are monads, you can combine them using Monad Transformers. This is a deep topic and requires quite a bit of learning if you are not familiar with it but the gist of such data structure would be something like the following:
class Task[E, A] {
def flatMap[U](f: A => Task[E, U]): Task[E, U] = ??? // Use monad transformer here.
}
where E represents the type of your custom errors - you probably will represent them through algebraic data type like sealed trait with lots of case class and A is your value type.
If your BodyParser throws an exception, or fails for some other reason, then any code inside the Action will not execute. The nature of async is irrelevant here.
For instance, the System.exit in the code below never runs as the BodyParser always returns an exception.
package controllers
import javax.inject.Inject
import play.api.mvc._
import scala.concurrent.Future
class Application #Inject() extends Controller {
def crud(param: String) = Action.async(
parse.error[String](Future.failed(new RuntimeException("oh noes!")))
) { req =>
System.exit(0)
???
}
}
This code generates this stacktrace:
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[RuntimeException: oh noes!]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:254) ~[play_2.11-2.4.0.jar:2.4.0]
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:180) ~[play_2.11-2.4.0.jar:2.4.0]
And the stacktrace says there is a DefaultHttpErrorHandler that gets called with exceptions thrown from a BodyParser.
The documentation for ScalaErrorHandling has examples of customising this, or writing your own one.
I'm overriding a method in spray-json in order to catch an exception it throws. In one particular case I want to add special handling, but otherwise I want to rethrow the original exception.
But the exception isn't rethrowing.
object MyObject extends DefaultJsonProtocol {
override def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = {
try {
super.fromField(value, fieldName)(reader)
} catch {
case e: DeserializationException =>
if (reader.isInstanceOf[SafeListFormat[_]]) EmptyList.asInstanceOf[T]
else deserializationError("Object is missing required member '" + fieldName + "'", e)
}
}
}
The deserializationError in spray-json: https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/package.scala
The method I'm overriding is fromField in here: https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/ProductFormats.scala
In debug mode I can see the original exception being caught, and my else case "rethrowing" the error, and even the deserializationError method being called in spray-json's library. But even through the DeserializationException is a RuntimeException the overridden method doesn't terminate.
I had wondered if there was something about deferred execution of the deserializationError method. But I even tried changing my exception handler from calling deserializationError to a simple throw e and that didnt work either.
What's going on here?
PS. I dont think this is specific to spray-json really, but is probably something interesting I dont know about scala. But I kept the example as a real world as possible just in case it is something particular to the way my overridden method is called by the spray-json library code.
EDIT: Raised same question with spray-user forum: https://groups.google.com/forum/#!topic/spray-user/YXtY6VyIVGk
It seem ok to me to be honest. So here go a few random observations (too long to put as comments):
Call super.fromField[T], right now you are not passing the T param. When things are weird, try to make everything explicit.
You can try functional error handling:
Try(super.fromField(value, fieldName)(reader)).recover {
case e: DeserializationException if (reader.isInstanceOf[SafeListFormat[_]]) => EmptyList.asInstanceOf[T]
case t: Exception => throw t
}.get
Note how I made the if part of the pattern match, doing that might help you insulate the problem.
I find it weird that your method does not terminate. You should probably try reducing the example to something very simple and start adding complexity little by little and see where it breaks.
Best of luck!
Is it possible for an exception to be thrown, escaping the Future context?
Since a Future "evaluates" as soon as it's defined:
is it possible for the definition of a Future to throw an exception, passing through the Future context?
scala> Future { Thread.sleep(3000); println("3 seconds elapsed"); 100 }
res2: scala.concurrent.Future[Int] =
scala.concurrent.impl.Promise$DefaultPromise#56113384
scala> 3 seconds elapsed
I could not come up with such an example.
scala> Future { throw new Exception("Foo!") }
res3: scala.concurrent.Future[Nothing] =
scala.concurrent.impl.Promise$DefaultPromise#47a86fbb
Yes, but maybe not quite the way you're thinking of. According to the source, only errors that are not NonFatal will escape Future.apply.
try Success(body) catch { case NonFatal(e) => Failure(e) }
i.e. exceptions like: VirtualMachineError, OutOfMemoryError,StackOverflowError, ThreadDeath, LinkageError, InterruptedException, ControlThrowable.. will go uncaught as they represent a fatal JVM error that you will not be able to handle.
A future in itself does nothing except define the computation. It just so happens to be you're using one of the constructors (or apply methods) that, by default, begins running the computation immediately. The mixing of exception handling and concurrency is one of the issues that, unfortunately, not explicit with scala.concurrent.Future. A better alternative may be to use scalaz.concurrent.Task which combines error handling with explicit concurrency.
Why you want to throw an error from your future?
Future is a monad which will handles latency and exceptions while you are dealing with it.
If you look at Future implementation it looks like as below,
trait Future[T] { def onComplete(callback: Try[T] => Unit)(implicit exe..) }
So when as soon as your future gets complete and value available your callback method will be invoked and return unit.
If you see callback method you will get idea your result would be success or error and Try[T] woluld manage all the things for you.
I have following scala code
def message(attachmentId: UUID) : URI = {
var r : mutable.MutableList[BasicTemplate] = new mutable.MutableList[BasicTemplate]
val t : Type = new TypeToken[Iterable[BasicTemplate]](){}.getType()
val m : String = "[{\"basicTemplate\":\"TEMPLATE\",\"baseline\":\"DEMO\",\"identifier\":\"0599999999\"}]"
r = new Gson().fromJson(m, t)
Console.println(r.head.getBasicTemplateName)
URI.create("http://google.com")
}
And it gives me following compilation error:
[ERROR] Class1.scala:402: error: dead code following this construct
[ERROR] r = new Gson().fromJson(m, t)
Any ideas why I get this error are highly appreciated!
Look at the signature of fromJson:
public <T> T fromJson(String json, Type typeOfT)
As you can see, this method has type parameter T, but you called it without specifying it. This way, type inferencer understood it as new Gson().fromJson[Nothing](m, t) and the entire expression was assigned the type Nothing.
In Scala, Nothing is a bottom type that is a subtype of all types and has no values. Methods that return Nothing are guaranteed to never return, either because they always throw an exception, fall into infinite loop, forcibly terminate the program (e.g. sys.exit()), etc. In your case, the fromJson call will cause a ClassCastException to be thrown when the JVM tries to cast its result to Nothing. Therefore, everything after that call is a dead code.
This type inference behaviour is different from Java, which would normally infer new Gson().<Object>fromJson(m, t) here.
object Reflects {
def mirror() = universe.runtimeMirror(getClass.getClassLoader)
def caseFields(x: AnyRef) = {
val instanceMirror = mirror().reflect(x)
instanceMirror.symbol.typeSignature.members.collect {
case m: MethodSymbol if (m.isCaseAccessor) => m.name.toString -> instanceMirror.reflectMethod(m).apply()
}
}
}
I define an object Reflects, when I invole caseFields method within other class
Sometimes this method throws following exception
java.lang.UnsupportedOperationException: tail of empty list
at scala.collection.immutable.Nil$.tail(List.scala:339) ~[scala-library.jar:na]
at scala.collection.immutable.Nil$.tail(List.scala:334) ~[scala-library.jar:na]
at scala.reflect.internal.SymbolTable.popPhase(SymbolTable.scala:172) ~[scala-reflect.jar:na]
And other strange exception.
What's wrong with this method
In 2.10.3 (and probably in 2.10.4, because it doesn't look like we're going to have time to backport the fix from 2.11.0-M7), runtime reflection isn't thread-safe: http://docs.scala-lang.org/overviews/reflection/thread-safety.html. Your stack trace is one of the multitude of possible manifestations of the problem.
Bad news is that in 2.10.x there's no workaround for the thread-unsafety issue apart from putting all reflective operations in a synchronized block. Good news is that in 2.11.0 the problem should be gone.