3

I have an EJB 3 module deployed on my Glassfish 2.1 server.

I'm trying to deploy a second EJB module, which depends on this first module, but the deployment fails with java.lang.NoClassDefFoundError about a class that can be found in the first EJB Module.

What's the best way to solve this dependecy between 2 EJB modules? I want to deploy them separately, and not have them in the same EAR.

More specifically, I have a Dependecy Injection of an EJB from my first EJB Module in one of my EJBs of my second EJB module:

@EJB (name="ejb/FirstEJB")
private FirstEJBRemote ejb;

But during deployment I get NoClassDefFoundError about the class FirstEJBRemote:

Error in annotation processing: java.lang.NoClassDefFoundError: FirstEJBRemote
Pearl Jade
  • 716
  • 1
  • 8
  • 22

3 Answers3

3

To understand why this exception has occurred, there are two things to grasp:

  1. How does one EJB reference another EJB
  2. EJB classloading

Let's first deal with number one, so let's name first EJB (containing FirstEJBRemote class) EJB_A and second EJB (who tries to access EJB_A's method) EJB_B. Not going too deeply into @LocalBean annotations and similar stuff, the only way for EJB_B to access EJB_A's methods is through interface. In other words, EJB_A implements some interface (in your case that is FirstEJBRemote) which EJB_B declares and through injection retrieves an instance of EJB_A. So far, so good.

Next, we have to understand EJB classloaders. Naturally, every external class/library that your application (in this case, EJB) uses in compile-time, has to be available also in the runtime. Otherwise, ClassNotFoundException will be thrown and that is exactly what happened in your case, because EJB_B is using FirstEJBRemote in compile-time:

private FirstEJBRemote ejb;

To provide this external class/library to the EJB in runtime, one has to do something of the following:

  • Put the class/library in the lib directory of the application server
  • Pack the class/library together with EJB
  • Put the class/library in the classpath of EAR containing your EJB

We will ignore third option because you said that you are not interested in EARs. Therefore, you can put FirstEJBRemote interface (in form of .jar file, of course) in lib directory of the Glassfish where it will be available both to EJB_A and EJB_B (thus, you don't have to pack it neither with EJB_A) or you can pack this interface together with EJB_B as well, taking care that it resides in the same package as in EJB_A.

In your comment, you wonder why is this "complicated" procedure needed at all. The answer is quite simple - if EJB classloaders weren't isolated mutually, every class that was deployed with EJB_A will be available to EJB_B. In this particular case, that would be wished behaviour, but imagine what happens when EJB_A includes version1 of some library (logging library, for example), and EJB_B uses this same library, but version2. Both classes would be loaded and you will encounter clash everywhere, ClassCastException etc. (or if you are lucky, EJB_B would "only" be forced to use version1 because that class would be already loaded by classloader).

Finally, an excerpt from Oracle GlassFish 3.1 Guide:

Circumventing Class Loader Isolation

Since each application or individually deployed module class loader universe is isolated, an application or module cannot load classes from another application or module. This prevents two similarly named classes in different applications or modules from interfering with each other.

Miljen Mikic
  • 14,765
  • 8
  • 58
  • 66
  • Isn't it enough that an EJB module is deployed on the server? It also has to be copied to external libraries directories so that other EJB modules can refer to it? – Pearl Jade May 11 '13 at 14:34
  • @Pearl Not an EJB module, but the interface that EJB implements. Otherwise, there is no way for second EJB to access FirstEJBRemote class (unless you pack that class together with second EJB, as mentioned in the answer). – Miljen Mikic May 11 '13 at 16:25
  • That sounds a bit too cumbersome, I'll wait to see if someone else has a better idea. I mean, what's the point in allowing you to deploy an EJB module separately if you're gonna have all this trouble to interact with it? – Pearl Jade May 12 '13 at 07:17
  • @Pearl I have completely rewritten my answer in order to explain it thoroughly. HTH. – Miljen Mikic May 12 '13 at 15:35
  • Thanks for all the details. I voted up on your answer but there was one a bit more accurate. – Pearl Jade May 12 '13 at 18:25
  • @Pearl You are welcome. Have you tried that other solution? Please see my comment on that question. – Miljen Mikic May 12 '13 at 20:29
  • Yes, I tried it and it works. your answer is good but for Glassfish that other answer is a bit more accurate. – Pearl Jade May 13 '13 at 12:26
  • @Pearl Once again (and for the last time, I promise), that is a bad practice. You should never put EJBs in applibs or any other server lib directory, only their interfaces. Otherwise interfaces/APIs don't make sense. – Miljen Mikic May 13 '13 at 16:05
  • you may have a point, but either way, with glassfish you need to put the interfaces' jar, which ever that is, in the lib/applibs directory, and not in the lib directory. And then you need to specify that jar as a library that a module will use, during deployment of that module. Otherwise they don't get loaded anyway. – Pearl Jade May 13 '13 at 18:26
  • @Pearl That is not true. You tagged your question with glassfish-2.x tag, and in Glassfish v2.x you can normally package external libraries and interfaces with EJB itself, without putting them in any server lib directory and everything will be loaded. To obtain that, right-click for example in Netbeans on your EJB project, then select `Properties->Build->Packaging->Package required JARs in EJB jar`. What you've mentioned is correct for Glassfish v3.1+. – Miljen Mikic May 13 '13 at 19:03
2

Miljen Mikic has a point about the whole Classloaders loading commonly named classes if they wouldn't be isolated.

But with Glassfish, what you should do in your case is place your First EJB module in the directory glassfish_home/domains/domain1/lib/applibs. Then during deployment of the Second EJB module you can specify that the First EJB module's jar should be loaded for this EJB module that is being deployed.

That's called Application-specific class loading, and here's more about it: http://docs.oracle.com/cd/E19798-01/821-1752/gatej/index.html

Henrique Ordine
  • 3,337
  • 4
  • 44
  • 70
  • This is a bad practice! Application-specific class loading is designed to load external libraries, not other EJBs! This way EJB_1 is not container-managed at all, what if e.g. implements a Web-service, that Web-service is not visible outside the application server. What if there is EJB_3 that invokes EJB_2, you will then put EJB_2 in applibs too? Can you find EJB_1 in JNDI registry? How would you inject it? To conclude - by putting EJB_1 only in applibs directory and not deploying it in standard way, it does not act as a EJB at all. – Miljen Mikic May 12 '13 at 20:28
  • I never said she shouldn't deploy her EJB module. Of course EJBs should be deployed to be EJBs. – Henrique Ordine May 13 '13 at 05:17
  • In that case, it is enough to put just FirstEJBRemote interface in applibs dir, as I suggested. You will this way load all classes twice unnecessarily. To make an analogy, if your standalone application requires java-ee-api-6.jar for example, you won't pack whole java-ee-6.jar with your application, but just the api. That is one of main purposes of separating interfaces and implementations. – Miljen Mikic May 13 '13 at 05:54
0

try this instead

@EJB(lookup ="JNDI_BEAN_NAME")

john miran
  • 393
  • 2
  • 13
  • I don't think this will help, it's the reference to the EJB's interface that can't be resolved during deployment, not the way it's being looked up at runtime. – Pearl Jade May 11 '13 at 11:59