Nicolas Ternisien created TOMEE-4293:
----------------------------------------
Summary: WAR-wide EJB local refs inserted into each EJB at startup
Key: TOMEE-4293
URL: https://issues.apache.org/jira/browse/TOMEE-4293
Project: TomEE
Issue Type: Bug
Components: TomEE Core Server
Affects Versions: 8.0.16
Reporter: Nicolas Ternisien
We're currently experimenting the migration of an EAR project from Weblogic
application server to TomEE.
While conduction this experiment, we also tried to see if we could rely on
"Collapsed EAR" approach, ie, simplifying the application by providing a single
WAR file with EJBs embedded into it, instead of multiple ejb and web modules
inside the EAR file.
The project is quite big with:
* 1992 EJBs in EJB Module (+ CDI beans)
* 1193 EJBs in Web Module (+ CDI beans for JAX RS resources)
But still, with the EAR approach, total loading time is about 1min, with 3.1 GB
JVM memory usage.
When trying the alternative approach via a WAR file (including the EJB module
as JAR), the startup takes huge time (30 to 40 minutes) and JVM memory
consumption goes up to 8 GB.
We tried to investigate this difference and we have identified the bottleneck
(via TomEE startup debugging) at JndiEncInfoBuilder, more especially at
JndiEncInfoBuilder.buildEjbRefs() where each EJB has the *whole* list of
ejbLocalRef of all the EJBs of the entire application.
I've setup a dedicated project (in attachment), with 2 different deployable
binaries:
* collapsedear-bug.war
* collapsedear-bug-ear.ear
Note the deployment is performed via the "tomee/apps" folder.
Both contains an EJB module, named "collapsedear-bug-ejb", which contains the
following EJB NumberService.java:
{code:java}
package org.lastnico.collapsedear.bug.ejb;
import java.util.Random;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
@Startup
public class NumberService {
private static Logger log = LoggerFactory.getLogger(NumberFinder.class);
@PostConstruct
public void start() {
log.info("Starting {}", this.getClass());
}
@EJB
private NumberFinder userFinder;
public long save() {
return new Random().nextLong();
}
}
{code}
To compare the two processes at loading time. Of course, this project is much
more simple (less than 10 EJBs in total), so we cannot notice it from memory
consumption or loading time, but here are the debugging output:
h4. collapsedear-bug.war
At startup, with a debugging breakpoint, watching for "NumberService" EJB
registration
at
JndiEncInfoBuilder.build(final JndiConsumer jndiConsumer, final String ejbName,
final String moduleId, final URI moduleUri, final JndiEncInfo moduleJndiEnc,
final JndiEncInfo compJndiEnc) throws OpenEJBException {
{code:java}
moduleId = collapsedear-bug-ejb
JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"
JndiConsumer.ejbLocalRef =
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null',
mappedName='null', lookupName='null'},
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder',
local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null',
mappedName='null', lookupName='null'},
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront',
local=org.lastnico.collapsedear.bug.war.CalculatorFront, link='null',
mappedName='null', lookupName='null'},
java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder',
local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null',
mappedName='null', lookupName='null'}}{code}
You can see that ejbLocalRef contains 8 different injection points, while
NumberService has only a single injection point (NumberFinder)
h4. collapsedear-bug-ear.ear
At the opposite, when debugging the startup of the equivalent EAR file
Here are the debugging info extracted:
{code}
moduleId = collapsedear-bug-ejb
JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"
JndiConsumer.ejbLocalRef =
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null',
mappedName='null', lookupName='null'}}
{code}
This time, as expected, the NumberService EJB only has 1 single EJB local ref,
matching its single injection point.
I suspect the reason for this is that MergeWebappJndiContext indistinctely
copies all possible EJB local refs, and hence each EJB receives the whole list
of injection points.
In the scenario of our application, we end up with
--
This message was sent by Atlassian Jira
(v8.20.10#820010)