To understand why this exception has occurred, there are two things to grasp:
- How does one EJB reference another EJB
- 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.