Sunday, January 22, 2012

Scala "Bug" with CDI Dependency Injection

“Sometimes”, Scala creates public final methods, although the .scala source defines
no public final method at all.
There are two things that CDI doesn’t like (which Scala “sometimes” generates):
1. public final methods
2. public fields
To investigate and reproduce these problems I created a scala-cdi project at GitHub.

public final method: The Bug

Referencing a parent field from a closure / inner class triggers this behavior:
@RequestScoped @Named class IndexBean { private lazy val log = LoggerFactory.getLogger(classOf[IndexBean]) def testExecutor() = { val executor = Executors.newFixedThreadPool(4); executor.submit(new Runnable() { override def run(): Unit = log.debug("Executor is running") }) } }
Compiles to:
$ javap -p IndexBean Compiled from "IndexBean.scala" public class com.soluvas.scalacdi.IndexBean extends java.lang.Object implements scala.ScalaObject{ private org.slf4j.Logger com$soluvas$scalacdi$IndexBean$$log; ... public final org.slf4j.Logger com$soluvas$scalacdi$IndexBean$$log();
Deploying this app in Weld will throw:
org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class com.soluvas.scalacdi.IndexBean is not proxyable because the type is final or it contains a final method public final org.slf4j.Logger com.soluvas.scalacdi.IndexBean.com$soluvas$scalacdi$IndexBean$$log() - Managed Bean [class com.soluvas.scalacdi.IndexBean] with qualifiers [@Any @Default @Named].         at org.jboss.weld.util.Proxies.getUnproxyableClassException(Proxies.java:225)         at org.jboss.weld.util.Proxies.getUnproxyableTypeException(Proxies.java:178)         at org.jboss.weld.util.Proxies.getUnproxyableTypesExceptionInt(Proxies.java:193)         at org.jboss.weld.util.Proxies.getUnproxyableTypesException(Proxies.java:167)         at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:110)

public final method: Workaround

Create a final local variable to hold the parent instance’s value:
def testExecutor() = { val executor = Executors.newFixedThreadPool(4); // this avoids 'log' becoming 'final' like: // private org.slf4j.Logger com$soluvas$scalacdi$IndexBean$$log; // public final org.slf4j.Logger com$soluvas$scalacdi$IndexBean$$log(); val log = this.log; executor.submit(new Runnable() { override def run(): Unit = log.debug("Executor is running") }) }
Which now compiles to:
$ javap -p IndexBean Compiled from "IndexBean.scala" public class com.soluvas.scalacdi.IndexBean extends java.lang.Object implements scala.ScalaObject{ private org.slf4j.Logger log; private org.slf4j.Logger log();

public field

I’m not able to reproduce this yet...
Tip: To learn more about Scala programming, I recommend Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.

2 comments:

  1. Great post for developers .I have bookmarked your page for the further reference Thanks for this valueable post.
    cheap Web Design

    ReplyDelete
  2. Wow what a post I am so impressed here can you more share here I will back to you soon as soon possible.
    Thanks for sharing...

    ReplyDelete