4

I am working on a framework which will be used in Java EE applications and therefore is likely to be deployed in the \lib directory of an EAR file.

The framework will use CDI to programmatically lookup and inject beans that are located in the Java EE application that is using the framework. The problem I've got is when the Provider.get() method from javax.enterprise.Provider<T> is called by my framework to get an instance of the bean, Weld throws a UnsatisfiedResolutionException.

To check this isn't an issue related to CDI I've also tried using MyClass myClass = Class.forName(clazz).newInstance(); to get an instance of the class but a ClassNotFoundException is thrown.

The structure of EAR file I'm using for testing purposes is as follows:

MyTestApp.ear
+\lib\MyFramework.jar <----Contains the framework invoking the Provider.get() method
+MyTestApp.jar        <----Contains the bean I want to inject

My test application's EAR contains an application.xml file which includes <library-directory>lib</library-directory>.

I believe this problem is occurring because the bean I want to inject exists in a separate classloader. i.e. \lib\MyFramework.jar is in a different classloader to MyTestApp.jar. I found this SO question which seems to suggest this is the case. Given that I'm developing a framework I don't believe the answer in the question is a viable solution for my needs.

I'm intrigued to find out whether creating a CDI portable extension would allow me to get an instance of the bean I want to use, but don't have enough experience in this area. Using @Observes ProcessAnnotatedType<T> I can see beans that exist outside of the \lib directory in an EAR file, including the ones I want to programmatically inject.

My questions are:

  1. Am I correct in assuming this problem is occurring because \lib\MyFramework.jar and MyTestApp.jar are in separate classloaders?

  2. Is there anything I can do using CDI that will allow my framework when deployed in the \lib directory of an EAR file to make the Provider.get() method call to avoid Weld throwing a UnsatisfiedResolutionException?

  3. Is there anything I can do outside of CDI to achieve same result?


Update

I've now tried moving MyFramework.jar to the root of the EAR file and also including the jar module in the application.xml file but the container fails to start the application due to a CDI unsatisfied dependency exception. The bean referenced in the exception can be injected when MyFramework.jar is located in the \lib directory and is a different bean to the one referenced in my question.

Community
  • 1
  • 1
Paul H
  • 2,104
  • 7
  • 39
  • 53
  • You mention Weld. What container are you deploying to? Include name and version. – John Ament Apr 12 '15 at 23:42
  • I've got two app servers that I'm deploying to. The first is GlassFish 4 which is running Weld 2.0.0 SP1. The second is GlassFish 4.1 which is running Weld 2.2.10 SP1. – Paul H Apr 13 '15 at 00:03

2 Answers2

0

1 : yes

2 : actually I don't know

3 : Yes, you must understand the ear classloader hierarchy, the jars in ear lib directory are loaded at the ear level and so available in all child classloaders (there is one child classloader per component in the ear).

It means that MyFramework.jar is visible from MyTestApp.jar ear child classloader but the inverse is false.

see In java EE, which jars should I put in the library dir?

You can either :

  • move MyTestApp.jar to ear lib directory (MyFramework.jar can be either in lib dir and reference MyTestApp.jar or at ear root)
  • move MyFramework.jar at ear root and reference MyTestApp.jar in its manifest classpath

see Deployment of multiple, depended CDI jars in one EAR

Community
  • 1
  • 1
Gab
  • 7,869
  • 4
  • 37
  • 68
  • One point that I'm a little confused on is why a CDI extension that is defined in `MyFramework.jar` is able scan beans outside its classloader (i.e. `MyTestApp.jar`) but a normal CDI managed bean in `MyFramework.jar` isn't able to inject beans outside its classloader (i.e. `MyTestApp.jar`). Any ideas why? – Paul H Apr 13 '15 at 18:07
  • No sry I'am not familar enough with CDI to understand the internal extension mechanics, I suppose that as it is designed to interact with third parties technologies it may use a classloader higher in the hierarchy to be aware of all components in the system. – Gab May 30 '15 at 11:06
0

It seems to me that it might be a poor architectural decision to have your "framework" depend on your application implementation. Perhaps it should be that your application that implements some framework-level interface to achieve your goal. As you have it, or would like it to work, suggests that an application, any application really, in your ear could influence the operation of all other applications in your ear by providing implementation to the framework. This seems like a bad idea to me. And if you were able to make this work and more that one application in your ear provided this implementation, CDI may still fail because of ambiguous dependencies.

There may be some container specific dependency configurations that you might be able employ. WildFly, for example, allows you to configure module-to-module dependencies. I think, it might be possible, in WildFly, for an ear to depend on a war.

While the above might work, after thinking about it, I this it is also a bad idea. You should really take a hard look at what you are really trying to do. I think if you take a harder look at what you are trying to do, you will discover that your will probably be able to peal out or abstract some dependencies and still provide implementation to the framework, but not from the applications in the ear.

Jason Holmberg
  • 291
  • 2
  • 17