“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
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)
Create a local final field 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();
I’m not able to reproduce this yet…