Glassfish4.1.1 (or Payara41) Java EE 7
[EDIT: The simple answer/solution might be "don't do it that way, use an EAR instead", but it has some disadvantages, see bottom]
I am exploring strategies for modularising a large web app. I have the following structure under a folder /multi-ant (noting all of the project modules are NetBeans Ant-style project versions):
multi-ant/
├── CoreAPIAnt (NetBeans Java Class Library project)
├── CoreEJBAnt (NetBeans EJB Module project)
├── CoreWebAnt (NetBeans Web Application Project, WAR + extra JAR build)
The system (as shown here) uses only @Local interfaces (there is also a @Remote aspect and standalone app client not shown).
CoreWebAnt can run standalone as a JSF Web App with WAR structure.
In CoreEJBAnt I have a single Element entity and:
@Stateless
public class ElementQuery implements IElementQuery {
Where IElementQuery is in CoreAPIAnt, which is a NetBeans Java Class Library project:
@Local
public interface IElementQuery extends Serializable {
In the CoreWebAnt JSF web app I have:
...
import javax.inject.Named;
import javax.faces.view.ViewScoped;
@Named(value = "elementManager")
@ViewScoped
public class ElementManager implements Serializable {
@EJB private IElementQuery elementQuery;
The aim is to have the web app modules EJB-implementation agnostic, but if I just set CoreAPIAnt as a project dependency (dist/CoreAPIAnt.jar) in CoreWebAnt (without reference to dist/CoreEJBAnt.jar) and then deploy CoreEJBAnt and the CoreWebAnt, the ElementManager used in CoreWebAnt can't resolve the injection against IElementQuery and I get this error:
Severe: Cannot resolve reference Local ejb-ref name=com.example.multi.core.web.ElementManager/elementQuery,Local 3.x interface =com.example.multi.core.api.IElementQuery,ejb-link=null,lookup=,mappedName=,jndi-name=,refType=Session
Severe: Exception while deploying the app [CoreWebAnt]
Severe: Exception during lifecycle processing
java.lang.RuntimeException: Cannot resolve reference Local ejb-ref name=com.example.multi.core.web.ElementManager/elementQuery,Local 3.x interface =com.example.multi.core.api.IElementQuery,ejb-link=null,lookup=,mappedName=,jndi-name=,refType=Session
at com.sun.enterprise.deployment.util.ComponentValidator.accept(ComponentValidator.java:374)
If I however also include CoreEJBAnt as a project dependency in CoreWebAnt (so dist/CoreEJBAnt.jar is available under its /lib) I can run CoreWebAnt fine completely standalone (and without first deploying CoreEJBAnt). But this defeats the entire purpose of having the interface-only CoreAPIAnt module.
Q: Why can't Glassfish "see" and resolve the injection implementation candidate @Stateless ElementQuery in the deployed CoreEJBAnt (vs @EJB IElementQuery from CoreAPIAnt used in CoreWebAnt) when CoreWebAnt is run separately and only dependent on CoreAPIAnt.jar ?
I wish if possible to avoid the overhead of @Remote (and all of the consequences of entities returned over RMI) and stay completely in a @Local development ecosystem for now.
Having to reference the CoreEJBAnt module from the web app(s) is not the end of the world, but it breaks the vs-API rule and I'd like to understand why it's also needed here.
EDIT: EAR approach (after more investigation)
If I create an empty EAR project in NetBeans (without a nested EJB module or Web App module), I can then use Add Java EE module on the Java EE Modules node to add CoreEJBAnt.jar and CoreWebAnt.war, and it runs fine without the CoreWebAnt module having a project dependency on the implementation module CoreEJBAnt (it just needs CoreAPIAnt). The exploded distribution structure is then like:
$ tree -L 3 CoreEAR/dist/gfdeploy/
CoreEAR/dist/gfdeploy/
└── CoreEAR
├── CoreEJBAnt_jar
│ ├── CoreAPIAnt.jar
│ ├── META-INF
│ ├── com ...
│ └── rebel.xml
├── CoreWebAnt_war
│ ├── META-INF
│ ├── WEB-INF
│ ├── index.xhtml
│ └── resources
├── META-INF
│ └── MANIFEST.MF
├── gfv3ee6.dpf
├── lib
│ ├── CoreAPIAnt.jar
│ └── other.jar
It even nicely pulls in other JAR libraries under lib I was also having to put in the lib of CoreWebAnt to get it to run standalone.
However, I then get some new errors when using JRebel, although it is definitely catching and hot reloading changes in edits in the CoreWebAnt and CoreEJBAnt projects.
So the answer to my question may indeed by "don't try to use the standalone web app, use the EAR approach", but I am still interested in knowing why Glassfish is not designed to let a web app "see" the separately deployed EJB module. I did not find anything specific about it in the specs.
The Java™ Platform, Enterprise Edition (Java EE) Specification, v7 provides a clear description of the requirements for runtime class visibility in the Application Assembly and Deployment section.
This is what all complete Java EE servers such as GlassFish and WildFly implement.
The isolation of separately deployed components (deployment units) is deliberate.