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)

Reply via email to