ScalatraServlet with AkkaSupport with GZipSupport

168 views
Skip to first unread message

Constantinos Kotsokalis

unread,
Jan 10, 2013, 10:58:04 AM1/10/13
to scalat...@googlegroups.com
Hi,

A Scalatra newbie here so please excuse ignorance if that's to blame!

I'm trying to use AkkaSupport & GZipSupport together, but it seems to fail. I've opened an issue on github but thought I'd try the list too. The code is the standard Akka & Scalatra example, extended with the GZipSupport trait:

package com.myapp

import _root_.akka.dispatch._
import _root_.akka.actor._

import org.scalatra._
import org.scalatra.akka.AkkaSupport

class myappServlet extends ScalatraServlet with AkkaSupport with GZipSupport {
  implicit val system = ActorSystem()

  get("/"){
    Future {
      <html><body>Hello Akka</body></html>
    }
  }
}

The stack trace:

[ERROR] [01/08/2013 17:41:42.274] [default-akka.actor.default-dispatcher-2] [akka.dispatch.Dispatcher] STREAM
    java.lang.IllegalStateException: STREAM
at org.eclipse.jetty.server.Response.getWriter(Response.java:699)
at org.scalatra.servlet.RichResponse.writer(RichResponse.scala:80)
at org.scalatra.ScalatraBase$$anonfun$renderPipeline$1.apply(ScalatraBase.scala:301)
at scala.PartialFunction$$anon$3.apply(PartialFunction.scala:97)
at scala.PartialFunction$$anon$3.apply(PartialFunction.scala:96)
at org.scalatra.ScalatraBase$class.loop$1(ScalatraBase.scala:275)
at org.scalatra.ScalatraBase$class.renderResponseBody(ScalatraBase.scala:277)
at org.scalatra.ScalatraServlet.renderResponseBody(ScalatraServlet.scala:50)
at org.scalatra.ScalatraBase$class.renderResponse(ScalatraBase.scala:245)
at com.myapp.myappServlet.org$scalatra$akka$AkkaSupport$$super$renderResponse(triptaoServlet.scala:9)
at org.scalatra.akka.AkkaSupport$$anonfun$renderResponse$1$$anonfun$apply$1.apply(AkkaSupport.scala:61)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
at org.scalatra.DynamicScope$class.withResponse(DynamicScope.scala:50)
at org.scalatra.ScalatraServlet.withResponse(ScalatraServlet.scala:50)
at org.scalatra.servlet.AsyncSupport$$anonfun$withinAsyncContext$1.apply(AsyncSupport.scala:25)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
at org.scalatra.DynamicScope$class.withRequest(DynamicScope.scala:41)
at org.scalatra.ScalatraServlet.withRequest(ScalatraServlet.scala:50)
at org.scalatra.servlet.AsyncSupport$class.withinAsyncContext(AsyncSupport.scala:24)
at com.myapp.myappServlet.withinAsyncContext(triptaoServlet.scala:9)
at org.scalatra.akka.AkkaSupport$$anonfun$renderResponse$1.apply(AkkaSupport.scala:58)
at org.scalatra.akka.AkkaSupport$$anonfun$renderResponse$1.apply(AkkaSupport.scala:56)
at akka.dispatch.Future$$anonfun$onSuccess$1.apply(Future.scala:484)
at akka.dispatch.Future$$anonfun$onSuccess$1.apply(Future.scala:483)
at akka.dispatch.DefaultPromise.akka$dispatch$DefaultPromise$$notifyCompleted(Future.scala:943)
at akka.dispatch.DefaultPromise$$anonfun$onComplete$1.apply$mcV$sp(Future.scala:937)
at akka.dispatch.Future$$anon$4$$anonfun$run$1.apply$mcV$sp(Future.scala:386)
at akka.dispatch.Future$$anon$4$$anonfun$run$1.apply(Future.scala:378)
at akka.dispatch.Future$$anon$4$$anonfun$run$1.apply(Future.scala:378)
at scala.util.DynamicVariable.withValue(DynamicVariable.scala:57)
at akka.dispatch.Future$$anon$4.run(Future.scala:378)
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:94)
at akka.jsr166y.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1381)
at akka.jsr166y.ForkJoinTask.doExec(ForkJoinTask.java:259)
at akka.jsr166y.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at akka.jsr166y.ForkJoinPool.runWorker(ForkJoinPool.java:1479)
at akka.jsr166y.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)

Am I doing something wrong or is this indeed  a bug?

 Costas


Ivan Porto Carrero

unread,
Jan 10, 2013, 11:42:54 AM1/10/13
to scalat...@googlegroups.com
that is a bug

-- 
Ivan Porto Carrero

Ivan Porto Carrero

unread,
Jan 10, 2013, 11:44:46 AM1/10/13
to scalat...@googlegroups.com
I think if you reorder the traits it will start to work, but that's a guess

extends ScalatraServlet with GZipSupport with AkkaSupport

-- 
Ivan Porto Carrero

On Thursday, 10 January 2013 at 16:58, Constantinos Kotsokalis wrote:

Costas Kotsokalis

unread,
Jan 10, 2013, 12:22:06 PM1/10/13
to scalat...@googlegroups.com
Hi Ivan,

I have tried this too, unfortunately it also fails.

 Costas

Ivan Porto Carrero

unread,
Jan 10, 2013, 12:37:18 PM1/10/13
to scalat...@googlegroups.com
you can get gzip support from jetty through a filter.


you need to add jetty-servlets to your dependencies

-- 
Ivan Porto Carrero

Costas Kotsokalis

unread,
Jan 10, 2013, 2:32:55 PM1/10/13
to scalat...@googlegroups.com
Hm, somehow this doesn't work. I've tried various things (also web.xml filter defs), ended up with:

    var fh = new FilterHolder(new org.eclipse.jetty.servlets.GzipFilter)
    fh.setName("compress")
    fh.setInitParameter("mimeTypes", "application/json")
    fh.setInitParameter("minGzipSize", "0")
    context.addFilter(fh, "/*", EnumSet.of(DispatcherType.ASYNC, DispatcherType.INCLUDE, DispatcherType.REQUEST))

Still, getting back uncompressed responses.

In any case, this now seems to be out of scope for this list so I'll take it to SO or something, it's not urgent anyway.
Hopefully the bug in GZipSupport will be fixed for 2.2.0 in the meanwhile, I saw you opened #249 (I had started #247, presumably it can now be closed or merged).

Thanks Ivan!

 Costas

Ivan Porto Carrero

unread,
Jan 10, 2013, 6:43:16 PM1/10/13
to scalat...@googlegroups.com
in build.sbt

libraryDependencies += "org.eclipse.jetty"          % "jetty-servlets"                 % "8.1.8.v20121106"     % "compile;container"


in ScalatraBootstrap.scala

val gzip = context.addFilter("gzip-responses", classOf[GzipFilter])
gzip.setInitParameter("mimeTypes", "text/html,text/plain,text/xml,application/json,application/xml,application/xhtml+xml,text/css,application/javascript,image/svg+xml")
gzip.addMappingForUrlPatterns(jutil.EnumSet.allOf(classOf[DispatcherType]), true, "/*")



I thought I saw that email before, the problem description looked very familiar. I'll close my ticket.

-- 
Ivan Porto Carrero

Constantinos Kotsokalis

unread,
Jan 17, 2013, 7:11:34 PM1/17/13
to scalat...@googlegroups.com
Posting a reply with findings so far in case anyone else is having the same problem. Always with a big fat disclaimer that I'm new to all these technologies so I may have missed something (or many things).

The code by Ivan doesn't work, but the problem is not in the code per se. Jetty and/or GZipFilter appear to ignore requests to gzip content if it comes from asynchronous operations; at least when Akka Futures/Promises are used. I've also tried sending output with a byte array, as per this comment on the scalatra issue opened, it didn't work either. When responses were coming from synchronous operations, everything was fine and gzipped content was returned.

From the things I've tried, the only thing that worked was Jetty continuations with GZipFilter enabled in web.xml. In this case content was sent back gzipped as expected even from async ops.
(I assume Ivan's code above would also work with continuations, although I didn't try).

  Costas

Tony Marjakangas

unread,
Jan 21, 2013, 5:23:46 AM1/21/13
to scalat...@googlegroups.com
I got a slightly modified version of Ivans GZipSupport to work with some changes in the scalatra core, but it still didnt gzip responses from Akka futures as if it did bypass the GZipSupport entirely.
I think that the best way to offer gzipped responses might be to build that into the core instead of a pluggable trait. You could potentially tell scalatra to gzip or not similar to how you set the contentType for a response.

/Tony

Ivan Porto Carrero

unread,
Jan 21, 2013, 5:25:42 AM1/21/13
to scalat...@googlegroups.com
I use this:

      val gzip = context.addFilter("gzip-responses", classOf[GzipFilter])
      gzip.setInitParameter("mimeTypes", "text/html,text/plain,text/xml,application/json,application/xml,application/xhtml+xml,text/css,application/javascript,image/svg+xml")
      gzip.addMappingForUrlPatterns(jutil.EnumSet.allOf(classOf[DispatcherType]), true, "/*")

And for me it gzips all.

-- 
Ivan Porto Carrero

Reply all
Reply to author
Forward
0 new messages