spray / twirl

The Play framework Scala template engine, stand-alone and packaged as an SBT plugin
Apache License 2.0
214 stars 20 forks source link

Twirl.settings breaks scala.collection.JavaConversions.mapAsScalaMap in Spring Application Context #19

Open sumdog opened 10 years ago

sumdog commented 10 years ago

I've got an old Scala app I'm slowing converting over to SBT. It still has some Spring 3 stuff in it (yea I know, I know. I will rip it out soon) and I have one thing in my application context that looks like so:

    <bean id="convertersAsScalaMap" class="scala.collection.JavaConversions" factory-method="mapAsScalaMap">
    <constructor-arg ref="converters"/>
</bean>

The trouble is, when I add Twirl.settings to the bottom of my build.sbt, my entire application breaks. I see this in my logs coming from Spring:

 Cannot resolve reference to bean 'convertersAsScalaMap' while setting bean property 'converters'; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [scala.collection.JavaConversions] for bean with name 'convertersAsScalaMap' defined in class path resource [io/bigsense/spring/spring.xml]; nested exception is java.lang.ClassNotFoundException: scala.collection.JavaConversions

I've narrowed it down to the Twirl.settings. If I comment it out, I can't use templates of course, but I no longer see this exception from calling the same set of beans. I'm on sbt 0.13.0 and Scala 2.10.3. My sbt-twirl plugin in 0.7.0.

Does the twirl plugin do something weird that makes JavaConversions inaccessible? I'd really like to use the Play2 template engine to replace my old JSPs.

Here is the source code: https://github.com/sumdog/BigSense/ (Revision 7431e80 and the one right before it on the reengineering branch)

jrudolph commented 10 years ago

I cannot think of something in twirl that would have this effect. What you can try to do to debug the issue you can run the server with the -verbose:class JVM option which will print a line when the JVM loads a class. Run it both in the working and in the broken case and see if the diff shows anything of interest.

jrudolph commented 10 years ago

You can look at https://github.com/spray/twirl/blob/master/sbt-twirl/src/main/scala/twirl/sbt/TwirlPlugin.scala to see which setting twirl adds. I'd say the most likely issue would be that the additional libraryDependency to twirl-api produces the issue for some reason. So you could just add the dependency to twirl-api without adding Twirl.settings, and see if that alone will induce the problem.

sumdog commented 10 years ago

It fails with the same class not found error if I add in just the following lines without Twirl.settings:

libraryDependencies <+= (scalaVersion) { sV =>
val scalaV = binaryScalaVersion(sV)
val crossVersionedName = "twirl-api_"+scalaV
val version = IO.readStream(getClass.getClassLoader.getResourceAsStream("twirl-version")).trim
"io.spray" % crossVersionedName % version
}

So in order to get the class load order, I added the following:

fork in run := true

javaOptions += "-verbose:class"

And in this state... Twirl.settings works fine! I can see java conversions in both class loader outputs as well.

Standard: http://penguindreams.org/files/classes_standard.txt Twirl Deps: http://penguindreams.org/files/classes_twirldep.txt

Looks like I might need to pass options to the sbt JVM to see if it's influencing how these classes are loaded. Forking the run process works for me though, so I'll just do that for now

jrudolph commented 10 years ago

Strange, maybe it's some interference between classloader stuff that spring is doing vs. running inside of the sbt process that is (just coincidentally?) triggered by adding the twirl-api reference. In any case thanks for posting the workaround.