Problem with Async / Futures / Akka, FileUpload and Swagger

203 visualizzazioni
Passa al primo messaggio da leggere

Mark Butler

da leggere,
6 ago 2014, 11:00:4906/08/14
a scalat...@googlegroups.com
Hi,

I am creating a simple REST API using Scalatra, Async / Futures / Akka support and Swagger.

Basically I need to upload a CSV file to a machine. The server then uses Futures / Akka to schedule doing something with that CSV file at some future time.

Generally this will be called by another machine (hence Swagger), but I want to make it possible for a human to do it as well for test purposes (hence using CSV rather than JSON).

However I was having a lot of trouble getting FileUpload to work. To begin with I thought this was due to Swagger. It did take me a little bit of time to get the Swagger UI to play nice with Scalatra, but that now seems to work. 

I think it is AsyncResult that is causing the problem. To demonstrate this I have put together a simplest possible example here


Specifically here


You can see two variants of the same post request, one that uses AsyncResult / Future, the other does not.

If you run the server from SBT:

    container:start

Then you can browse using Swagger-UI: http://localhost:8080/swagger

You see that the version without the future works fine, but the future version fails with this exception:

    java.io.FileNotFoundException: /tmp/jetty-0.0.0.0-8080-webapp-_-any-8266606499060789825.dir/MultiPart1494068154862294142 (No such file or directory)
   at java.io.FileInputStream.open(Native Method)
   at java.io.FileInputStream.<init>(FileInputStream.java:146)
   at org.eclipse.jetty.util.MultiPartInputStreamParser$MultiPart.getInputStream(MultiPartInputStreamParser.java:211)
   at org.scalatra.servlet.FileItem.getInputStream(FileUploadSupport.scala:253)
   at com.pataniqa.example.MyScalatraServlet$$anonfun$2$$anon$1$$anonfun$3.apply(MyScalatraServlet.scala:61)
   at com.pataniqa.example.MyScalatraServlet$$anonfun$2$$anon$1$$anonfun$3.apply(MyScalatraServlet.scala:58)
   at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
   at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
   at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:41)
   at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:393)
   at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
   at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
   at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
   at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Strangely though if I keep trying the future version, sometimes it succeeds, but generally it fails?

Is my use of AsyncResult / Future correct?

If it is correct is there is a workaround for this?

Thanks very much for Scalatra, Scalatra Swagger and Akka support.

Mark


Ivan Porto Carrero

da leggere,
6 ago 2014, 11:10:3206/08/14
a scalat...@googlegroups.com
The code looks correct.
As workaround I would say for uploads you typically don't need to be in async mode to receive the file so you can do the fileParams.get call outside of the async result.


I created this issue to track it: https://github.com/scalatra/scalatra/issues/412

Mark Butler

da leggere,
6 ago 2014, 12:14:4706/08/14
a scalat...@googlegroups.com
Thanks CasualJim / Ivan !

The problem seems to be the AsyncResult block - I had read this comment

AsyncResult isn't strictly necessary. It's a way to ensure that if you close your Future over mutable state (such as a request object or a var) that the state is captured at the point you hand off to the Future.

If you attempt to use mutable state inside your Future without AsyncResult (e.g. callingrequest.headers or something), you'll get an exception. If you use AsyncResult, it'll work. So, you're trading a bit of boilerplate code for a bit of safety. If you can remember not to close over mutable state, don't bother with AsyncResult.

Here

http://www.scalatra.org/2.3/guides/async/akka.html

So because I call a future after the file has uploaded, I assumed I needed the AsyncResult block. 

If I omit it everything seems to work if I make sure I am using val's in my future.

Very best!


Mark





--
You received this message because you are subscribed to a topic in the Google Groups "scalatra-user" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/scalatra-user/U1rZ00xL14w/unsubscribe.
To unsubscribe from this group and all its topics, send an email to scalatra-use...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rispondi a tutti
Rispondi all'autore
Inoltra
0 nuovi messaggi