Tip: To learn more about Scala programming, I recommend Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.
Scala programming language, Scala tutorials, Scala resources, using Scala with Eclipse IDE / NetBeans / Maven / Java EE / JSF / JPA
Monday, February 27, 2012
Scala Source Code and Density
Wednesday, February 15, 2012
UnproxyableResolutionException Workaround when using Scala Closures and javax.inject CDI Beans Together
For example, this "innocent" code will not work:
@Inject private var fbPhotoImporterFactory: Instance[FacebookPhotoImporter] = _
@Produces @Named("facebookPhotoImporter") private var fbPhotoImporter: ActorRef = _
@PostConstruct
def init() {
logger.debug("Starting FacebookPhotoImporter actor")
fbPhotoImporter = Actor.actorOf(fbPhotoImporterFactory.get())
fbPhotoImporter.start()
}
It will throw:
Caused by: org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001437 Normal scoped bean class com.satukancinta.web.Persistence is not proxyable because the type is final or it contains a final method public final javax.enterprise.inject.Instance com.satukancinta.web.Persistence.com$satukancinta$web$Persistence$$fbPhotoImporterFactory() - Managed Bean [class com.satukancinta.web.Persistence] with qualifiers [@Any @Default].
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)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:126)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:345)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:330)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:366)
at org.jboss.as.weld.WeldContainer.start(WeldContainer.java:82)
at org.jboss.as.weld.services.WeldService.start(WeldService.java:89)
... 5 more
As you can see, my use code isn't exactly "edge cases".
It's actually a pretty common use case: create a Akka actor and pass a factory function to it, as a closure.
The code above doesn't look like it's using a closure, but it actually is when written like this: (same functionality, but still breaks CDI)
fbPhotoImporter = Actor.actorOf { fbPhotoImporterFactory.get() }
I can see why CDI has a strict requirement, and I can also understand why Scala implements it the way it is (Scala developers definitely already has a lot of problems working around powerful Scala features into a very restrictive JVM bytecode requirements). This is the price we pay for having a somewhat inferior language (Java, please don't get offended) in the first place.
But I as an application developer want to have a quick fix for this issue. Re-coding the class in plain Java is one option, but it turns I don't need to. There is a workaround, by creating a helper method then using it:
@Inject private var fbPhotoImporterFactory: Instance[FacebookPhotoImporter] = _
@Produces @Named("facebookPhotoImporter") private var fbPhotoImporter: ActorRef = _
def createFbPhotoImporter() = fbPhotoImporterFactory.get()
@PostConstruct
def init() {
logger.debug("Starting FacebookPhotoImporter actor")
fbPhotoImporter = Actor.actorOf(createFbPhotoImporter)
fbPhotoImporter.start()
}
Now Scala is happy and CDI is also happy. Yes it's a bit more verbose but not too bad. And I guess the code is now somewhat more understandable for Java guys. :)
Tip: To learn more about Scala programming, I recommend Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.