Some powerful Scala programming language features like closures, pattern matching, and lazy vals don't work well with dependency injection frameworks like javax.inject aka CDI. This is due to stricter class structure requirements to enable proxying. 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.
@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.
No comments:
Post a Comment