Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClassCastException - Loose app deployment incorrect for EAR configured with <skinnyModules>true</skinnyModules> #1855

Open
mthmulders opened this issue Jan 2, 2025 · 17 comments · May be fixed by #1861

Comments

@mthmulders
Copy link

Describe the bug
I have a project that contains a web application and a JAX-RS API, packaged together in an EAR. Both consume a shared library that is built with JPA. Running the applications yields a runtime exception in the web app or the API, but I cannot predict in which one.

The error is this:

jakarta.servlet.ServletException: class it.mulders.ol.jpa.ThingEntity cannot be cast to class it.mulders.ol.jpa.ThingEntity (it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @47e65b4f; it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @21e61371)
at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:255)
at [internal classes]
Caused by: java.lang.ClassCastException: class it.mulders.ol.jpa.ThingEntity cannot be cast to class it.mulders.ol.jpa.ThingEntity (it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @47e65b4f; it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @21e61371)

[skip]

at it.mulders.ol.jpa.JpaThingRepository.findByName(JpaThingRepository.java:31)

[skip]

at it.mulders.ol.webapp.IndexView.(IndexView.java:26)

Full stack trace:

[2025-01-02T22:10:07.262+0100] 0000005e com.ibm.ws.webcontainer.util.ApplicationErrorUtils           E SRVE0777E: Exception thrown by application class 'jakarta.faces.webapp.FacesServlet.service:255'
jakarta.servlet.ServletException: class it.mulders.ol.jpa.ThingEntity cannot be cast to class it.mulders.ol.jpa.ThingEntity (it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @47e65b4f; it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @21e61371)
	at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:255)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1266)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:754)
	at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:451)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.invokeTarget(WebAppFilterChain.java:197)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:100)
	at com.ibm.ws.security.jaspi.JaspiServletFilter.doFilter(JaspiServletFilter.java:58)
	at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:203)
	at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:93)
	at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:1069)
	at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1260)
	at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:5096)
	at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.handleRequest(DynamicVirtualHost.java:328)
	at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:1047)
	at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:293)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1260)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:476)
	at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:435)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:569)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:503)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:363)
	at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:330)
	at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:169)
	at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:77)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:516)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:586)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:970)
	at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1059)
	at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:298)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1575)
Caused by: java.lang.ClassCastException: class it.mulders.ol.jpa.ThingEntity cannot be cast to class it.mulders.ol.jpa.ThingEntity (it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @47e65b4f; it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @21e61371)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:215)
	at java.base/java.util.Vector$VectorSpliterator.tryAdvance(Vector.java:1449)
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:147)
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:588)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:574)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
	at java.base/java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:692)
	at it.mulders.ol.jpa.JpaThingRepository.findByName(JpaThingRepository.java:31)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
	at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:106)
	at it.mulders.ol.domain.ThingRepository$1657707365$Proxy$_$$_WeldClientProxy.findByName(Unknown Source)
	at it.mulders.ol.webapp.IndexView.<init>(IndexView.java:26)
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:501)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:485)
	at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:119)
	at org.jboss.weld.injection.ConstructorInjectionPoint.invokeAroundConstructCallbacks(ConstructorInjectionPoint.java:92)
	at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:78)
	at org.jboss.weld.injection.producer.AbstractInstantiator.newInstance(AbstractInstantiator.java:28)
	at org.jboss.weld.injection.producer.BasicInjectionTarget.produce(BasicInjectionTarget.java:112)
	at org.jboss.weld.injection.producer.BeanInjectionTarget.produce(BeanInjectionTarget.java:186)
	at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:160)
	at org.jboss.weld.util.bean.IsolatedForwardingBean.create(IsolatedForwardingBean.java:45)
	at org.jboss.weld.contexts.AbstractContext.get(AbstractContext.java:96)
	at org.jboss.weld.contexts.PassivatingContextWrapper$AbstractPassivatingContextWrapper.get(PassivatingContextWrapper.java:84)
	at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
	at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.get(ContextualInstanceStrategy.java:177)
	at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:679)
	at org.jboss.weld.module.web.el.AbstractWeldELResolver.lookup(AbstractWeldELResolver.java:107)
	at org.jboss.weld.module.web.el.AbstractWeldELResolver.getValue(AbstractWeldELResolver.java:90)
	at jakarta.el.CompositeELResolver.getValue(CompositeELResolver.java:62)
	at org.apache.el.parser.AstIdentifier.getValue(AstIdentifier.java:94)
	at org.apache.el.parser.AstValue.getValue(AstValue.java:137)
	at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190)
	at org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression.getValue(ContextAwareTagValueExpression.java:100)
	at org.apache.myfaces.view.facelets.el.ELText$ELTextVariable.writeText(ELText.java:238)
	at org.apache.myfaces.view.facelets.el.ELText$ELTextComposite.writeText(ELText.java:137)
	at org.apache.myfaces.view.facelets.compiler.TextInstruction.write(TextInstruction.java:45)
	at org.apache.myfaces.view.facelets.compiler.UIInstructions.encodeBegin(UIInstructions.java:46)
	at org.apache.myfaces.view.facelets.compiler.UILeaf.encodeAll(UILeaf.java:362)
	at jakarta.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:519)
	at jakarta.faces.component.UIComponentBase.encodeAll(UIComponentBase.java:519)
	at org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage.renderView(FaceletViewDeclarationLanguage.java:1783)
	at org.apache.myfaces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:316)
	at jakarta.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:74)
	at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:122)
	at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:241)
	at jakarta.faces.webapp.FacesServlet.service(FacesServlet.java:225)
	... 31 more

Steps to Reproduce

  • Clone mthmulders/openliberty-multi-app-deploy.
  • Prepare a PostgreSQL database named postgres with username postgres, password postgres.
  • Prepare a table in the database:
    create table public.thing (
      id uuid not null,
      name character varying not null,
      description character varying not null
    );
  • Populate at least one row in the database table:
    insert into thing
    (id, name, description)
    values 
    ('0ac696b2-a53d-458a-9f82-44d0c47fc9Cb', 'whatever', 'Whatever')
  • Run mvn liberty:dev.
  • Navigate to http://localhost:9080/index.xhtml
  • Navigaate to http://localhost:9080/api/status

Expected behavior
Two WAR modules inside an EAR can share JPA-based code without interfering with each other.

Diagnostic information:

  • OpenLiberty Version: 24.0.0.12
  • Affected feature(s) persistence-3.1
  • Java Version:
    openjdk version "23.0.1" 2024-10-15
    OpenJDK Runtime Environment Temurin-23.0.1+11 (build 23.0.1+11)
    OpenJDK 64-Bit Server VM Temurin-23.0.1+11 (build 23.0.1+11, mixed mode, sharing)
    
  • server.xml configuration (WITHOUT sensitive information like passwords)
    <?xml version="1.0" encoding="UTF-8"?>
    <server description="traqqr-dev-server">
       <featureManager>
           <feature>appSecurity-5.0</feature>
           <feature>beanValidation-3.0</feature>
           <feature>cdi-4.0</feature>
           <feature>expressionLanguage-5.0</feature>
           <feature>faces-4.0</feature>
           <feature>jsonb-3.0</feature>
           <feature>persistence-3.1</feature>
           <feature>restfulWS-3.1</feature>
           <feature>servlet-6.0</feature>
       </featureManager>
    
       <variable name="jdbc.hostname" defaultValue="localhost" />
       <variable name="jdbc.database" defaultValue="postgres" />
       <variable name="jdbc.username" defaultValue="postgres" />
       <variable name="jdbc.password" defaultValue="postgres" />
    
       <library id="postgresql-jdbc">
           <fileset dir="${server.config.dir}/jdbc/" includes="postgresql*jar" />
       </library>
    
       <dataSource id="example-datasource" jndiName="jdbc/example-ds" transactional="true" type="javax.sql.XADataSource">
           <jdbcDriver libraryRef="postgresql-jdbc" />
           <properties serverName="${jdbc.hostname}"
                       databaseName="${jdbc.database}"
                       portNumber="5432"
                       user="${jdbc.username}"
                       password="${jdbc.password}" />
       </dataSource>
    
       <application location="multi-app.ear" name="multi-app" />
    
       <applicationMonitor dropinsEnabled="false" />
       <logging isoDateFormat="true" />
       <webContainer deferServletLoad="false" />
       <cdi12 enableImplicitBeanArchives="false" />
    </server>
  • If it would be useful, upload the messages.log file found in $WLP_OUTPUT_DIR/messages.log

Additional context
I have added some debug logging in the code that leverages JPA and I noticed this:

Invocation from the web app

[INFO] 2025-01-02 22:10:06,750  INFO                 it.mulders.ol.jpa.JpaThingRepository -- ThingEntity lives in classloader com.ibm.ws.classloading.internal.AppClassLoader@21e61371
[INFO] jakarta.servlet.ServletException: class it.mulders.ol.jpa.ThingEntity cannot be cast to class it.mulders.ol.jpa.ThingEntity (it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @47e65b4f; it.mulders.ol.jpa.ThingEntity is in unnamed module of loader com.ibm.ws.classloading.internal.AppClassLoader @21e61371)

Invocation from the API

[INFO] 2025-01-02 22:10:29,934  INFO                 it.mulders.ol.jpa.JpaThingRepository -- ThingEntity lives in classloader com.ibm.ws.classloading.internal.AppClassLoader@47e65b4f

I think it is interesting to note that the error message from the web app refers to the classloader from the API.

@ajaypaul-ibm
Copy link

Hi @mthmulders, I am currently analysing the issue and have successfully reproduced it. I will keep you updated on my progress.

@ajaypaul-ibm
Copy link

This seems like a Class loader problem in Liberty . Tagging @tjwatson and @dazavala for further insight.

@tjwatson
Copy link
Member

tjwatson commented Jan 3, 2025

The issue is the jpa-1.jar with the class it.mulders.ol.jpa.ThingEntity is still contained in each WAR's WEB-INF/lib folder making it not shared between the two WARs. The issue is the jpa dependency is not declared for the EAR's pom.xml to get it properly placed int he EAR's lib/ folder.

See my PR to your repo: mthmulders/openliberty-multi-app-deploy#1

@mthmulders
Copy link
Author

Thank you @ajaypaul-ibm and @tjwatson for your investigations.

@tjwatson, I've merged your suggestion, ran mvn clean, then mvn package and finally mvn liberty:dev.

After mvn package, I verified that the jpa-1.jar is now in the EAR's lib/ folder, and that both JAR's don't have it included anymore:

  • Content of unzipped EAR
    $ tree
    .
    ├── META-INF
    │   ├── MANIFEST.MF
    │   ├── application.xml
    │   └── maven
    │       └── it.mulders.ol
    │           └── deployment
    │               ├── pom.properties
    │               └── pom.xml
    ├── it.mulders.ol-api-1.war
    ├── it.mulders.ol-webapp-1.war
    └── lib
        ├── ch.qos.logback-logback-classic-1.5.12.jar
        ├── ch.qos.logback-logback-core-1.5.12.jar
        ├── it.mulders.ol-domain-1.jar
        ├── it.mulders.ol-jpa-1.jar
        └── org.slf4j-slf4j-api-2.0.16.jar
    
    6 directories, 11 files
    
  • Inspecting the WAR's inside the EAR
    $ find . -name "*.war" -exec unzip -l {} \;
    
    Archive:  ./it.mulders.ol-api-1.war
      Length      Date    Time    Name
    ---------  ---------- -----   ----
          283  02-01-1980 00:00   META-INF/MANIFEST.MF
            0  02-01-1980 00:00   META-INF/
            0  02-01-1980 00:00   WEB-INF/
            0  02-01-1980 00:00   WEB-INF/classes/
            0  02-01-1980 00:00   WEB-INF/classes/it/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/api/
            0  02-01-1980 00:00   WEB-INF/lib/
          288  02-01-1980 00:00   WEB-INF/beans.xml
          420  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/api/ExampleApplication.class
          712  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/api/RandomNumberGenerator.class
         1696  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/api/StatusDTO.class
         1978  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/api/StatusResource.class
          366  02-01-1980 00:00   WEB-INF/classes/logback.xml
         1735  02-01-1980 00:00   META-INF/maven/it.mulders.ol/api/pom.xml
           47  02-01-1980 00:00   META-INF/maven/it.mulders.ol/api/pom.properties
    ---------                     -------
         7525                     17 files
    Archive:  ./it.mulders.ol-webapp-1.war
      Length      Date    Time    Name
    ---------  ---------- -----   ----
          283  02-01-1980 00:00   META-INF/MANIFEST.MF
            0  02-01-1980 00:00   META-INF/
            0  02-01-1980 00:00   WEB-INF/
            0  02-01-1980 00:00   WEB-INF/classes/
            0  02-01-1980 00:00   WEB-INF/classes/it/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/
            0  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/webapp/
            0  02-01-1980 00:00   WEB-INF/lib/
          288  02-01-1980 00:00   WEB-INF/beans.xml
         1457  02-01-1980 00:00   WEB-INF/classes/it/mulders/ol/webapp/IndexView.class
          366  02-01-1980 00:00   WEB-INF/classes/logback.xml
          353  02-01-1980 00:00   WEB-INF/faces-config.xml
          407  02-01-1980 00:00   index.xhtml
         1393  02-01-1980 00:00   web.xml
         2065  02-01-1980 00:00   META-INF/maven/it.mulders.ol/webapp/pom.xml
           50  02-01-1980 00:00   META-INF/maven/it.mulders.ol/webapp/pom.properties
    ---------                     -------
         6662                     17 files
    

But alas, I am still facing the same error message.

@tjwatson
Copy link
Member

tjwatson commented Jan 3, 2025

Turns out this is now a liberty plugin issue with the generated loose application XML. The liberty maven plugin is not aware of the <skinnyModules>true</skinnyModules> configuration for the production of the EAR. This means the generation of the file target/liberty/wlp/usr/servers/defaultServer/apps/multi-app.ear.xml is not correct. For example, see the section for the it.mulders.ol-api-1.war WAR

    <archive targetInArchive="/it.mulders.ol-api-1.war">
        <dir sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/api/src/main/webapp" targetInArchive="/"/>
        <dir sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/api/target/classes" targetInArchive="/WEB-INF/classes"/>
        <file sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/api/target/tmp/META-INF/MANIFEST.MF" targetInArchive="/META-INF/MANIFEST.MF"/>
        <archive targetInArchive="/WEB-INF/lib/domain-1.jar">
            <dir sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/domain/target/classes" targetInArchive="/"/>
            <file sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/domain/target/tmp/META-INF/MANIFEST.MF" targetInArchive="/META-INF/MANIFEST.MF"/>
        </archive>
        <archive targetInArchive="/WEB-INF/lib/jpa-1.jar">
            <dir sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/jpa/target/classes" targetInArchive="/"/>
            <file sourceOnDisk="/home/tjwatson/dev/liberty/examples/openliberty-multi-app-deploy/jpa/target/tmp/META-INF/MANIFEST.MF" targetInArchive="/META-INF/MANIFEST.MF"/>
        </archive>
        <file sourceOnDisk="/home/tjwatson/.m2/repository/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar" targetInArchive="/WEB-INF/lib/slf4j-api-2.0.16.jar"/>
        <file sourceOnDisk="/home/tjwatson/.m2/repository/ch/qos/logback/logback-classic/1.5.12/logback-classic-1.5.12.jar" targetInArchive="/WEB-INF/lib/logback-classic-1.5.12.jar"/>
        <file sourceOnDisk="/home/tjwatson/.m2/repository/ch/qos/logback/logback-core/1.5.12/logback-core-1.5.12.jar" targetInArchive="/WEB-INF/lib/logback-core-1.5.12.jar"/>
    </archive>

It still has entries for the domain and jpa JARs even though they are also in the lib/ of the EAR.

We will need to move this issue over to liberty maven plugin.

@tjwatson
Copy link
Member

tjwatson commented Jan 3, 2025

If you want to see it "work" then you can copy the produced EAR file deployment/target/multi-app.ear to deployment/target/liberty/wlp/usr/servers/defaultServer/apps/ and delete the file deployment/target/liberty/wlp/usr/servers/defaultServer/apps/multi-app.ear.xml

Then you can start the server (without using mvn) with:
./deployment/target/liberty/wlp/bin/server run

@scottkurz scottkurz changed the title Running two apps with the same JPA code fails with java.lang.ClassCastException ClassCastException - Loose app deployment incorrect for EAR configured with <skinnyModules>true</skinnyModules> Jan 3, 2025
@scottkurz scottkurz transferred this issue from OpenLiberty/open-liberty Jan 3, 2025
@scottkurz
Copy link
Member

Thanks for the investigation @tjwatson . Just searching the code quickly I can see that our loose app deployment seems to have support for the <skinnyWars> configuration parm of maven-ear-plugin but NOT for <skinnyModules> .

It seems worth moving the issue over to our liberty-maven-plugin repo (which I did) then to investigate further. Thanks.

@mthmulders
Copy link
Author

@tjwatson @scottkurz thanks for your investigations! I can confirm it works as expected if I don't use the liberty-maven-plugin and manually start OpenLiberty, as suggested by @scottkurz.

@scottkurz
Copy link
Member

scottkurz commented Jan 6, 2025

@tjwatson @scottkurz thanks for your investigations! I can confirm it works as expected if I don't use the liberty-maven-plugin and manually start OpenLiberty, as suggested by @scottkurz.

And note it is still possible to use liberty-maven-plugin to deploy with loose app disabled, (either -DlooseApplication=false or plugin config <looseApplication>false</looseApplication> ). It's not as convenient, you have to add the goals to the lifecycle yourself instead of using dev mode (or use dev mode with some non-obvious workarounds). If this sounds like something you want to try, though, and you need help with the workaround feel free to let us know.

@mthmulders
Copy link
Author

And note it is still possible to use liberty-maven-plugin to deploy with loose app disabled, (either -DlooseApplication=false or plugin config <looseApplication>false</looseApplication> ). It's not as convenient, you have to add the goals to the lifecycle yourself instead of using dev mode (or use dev mode with some non-obvious workarounds). If this sounds like something you want to try, though, and you need help with the workaround feel free to let us know.

For the time being, I'd love to have some hints on this. I tried writing some convenience scripts to speed up the build + restart Open Liberty cycle, but the feedback loop is considerably shorter than when I use mvn liberty:dev.

From what I understand, I guess it starts with setting looseApplication to false and then running mvn package -pl :artifact-with-ear in a second terminal?

@tjwatson
Copy link
Member

tjwatson commented Jan 6, 2025

Could you consider using <skinnyWars> for now? That would not make your RARs or EJBs contained directly in the EAR "skinny". In practice I am unsure that is a real problem since EJB and RAR modules in EARs get placed on the EAR class loader already (including their Class-Path references).

@mthmulders
Copy link
Author

Could you consider using <skinnyWars> for now?

I don't use RARs or EJBs in my project, and I don't plan to use them, so it shouldn't be a problem.

However, I've just tried to replace skinnyModules with skinnyWars in the Maven EAR Plugin configuration. Running mvn liberty:dev -pl :deployment -am prints out

[INFO] [WARNING ] CWNEN0047W: Resource annotations on the fields of the it.mulders.ol.webapp.IndexView class will be ignored. The annotations could not be obtained because of the exception : java.lang.NoClassDefFoundError: org/slf4j/Logger
[INFO] [WARNING ] CWNEN0047W: Resource annotations on the fields of the it.mulders.ol.api.StatusResource class will be ignored. The annotations could not be obtained because of the exception : java.lang.NoClassDefFoundError: org/slf4j/Logger

And both applications don't get deployed. The contents of the EAR look good, though, and as you can see, SLF4J is indeed in the lib/ folder of the EAR:

.
├── META-INF
│   ├── MANIFEST.MF
│   ├── application.xml
│   └── maven
│       └── it.mulders.ol
│           └── deployment
│               ├── pom.properties
│               └── pom.xml
├── it.mulders.ol-api-1.war
├── it.mulders.ol-webapp-1.war
└── lib
    ├── ch.qos.logback-logback-classic-1.5.12.jar
    ├── ch.qos.logback-logback-core-1.5.12.jar
    ├── it.mulders.ol-domain-1.jar
    ├── it.mulders.ol-jpa-1.jar
    └── org.slf4j-slf4j-api-2.0.16.jar

6 directories, 11 files

@tjwatson
Copy link
Member

tjwatson commented Jan 6, 2025

Does that happen with the produced EAR archive also? If so then my understanding of how skinnyWars works for the maven EAR production is not correct and you would need to continue to use skinnyModules. If the EAR archive works vs. the loose application XML then this indicates a separate issue with how the liberty maven plugin handles skinnyWars.

@mthmulders
Copy link
Author

Oops, I noticed I didn't set looseApplication to false an hour ago. Now I did. Still didn't work. After that, I double-checked the generated multi-app.ear.xml and I noticed there were a few entries like this

<archive targetInArchive="/lib//it.mulders.ol-domain-1.jar">

Notice the double / in the path!

I then checked the defaultLibBundleDir parameter for the Maven EAR Plugin and it had the value lib/. I've removed that trailing / and now it seems to be working correctly.

This improves the feedback cycle already, as I now only have to run mvn package -pl :deployment -am in a second terminal and no longer need to restart Open Liberty completely. Still, it's not on par with using mvn liberty:dev alone and having an immediate reload when you change the code.

So to summarise, I went from skinnyModules=true to skinnyWars=true in the Maven EAR Plugin and added looseApplication=false in the Liberty Maven Plugin.

Am I missing something?

@scottkurz
Copy link
Member

So to summarise, I went from skinnyModules=true to skinnyWars=true in the Maven EAR Plugin and added looseApplication=false in the Liberty Maven Plugin.

If you've switched from skinnyModules=true to skinnyWars=true then you should be able to use loose apps (which defaults to true).

This seemed to work fine when I tested your recreate app repo. Though I'm a bit confused because I noticed it seemed to work just fine for me on the initial commit too (back when the jpa-1 JAR was packaged within each WAR). I wonder if there is some other variable or condition in the use case required to recreate the problem, and/or maybe there's something related to the CDI scope in play? It's also possible that my inability to recreate really doesn't matter, ultimately, with any of the points discussed here.. (but thought it was worth mentioning).

@mthmulders
Copy link
Author

Sorry for not getting back earlier.

If you've switched from skinnyModules=true to skinnyWars=true then you should be able to use loose apps (which defaults to true).

I've just tried to do this. In the reproducer that I shared, it seems to work. But in a larger project (which is also open source, but because it's so large I first created a small reproducer) it still doesn't work. As far as I can tell, all relevant Maven and OpenLiberty configuration is the same in both projects.

This seemed to work fine when I tested your recreate app repo. Though I'm a bit confused because I noticed it seemed to work just fine for me on the initial commit too (back when the jpa-1 JAR was packaged within each WAR). I wonder if there is some other variable or condition in the use case required to recreate the problem, and/or maybe there's something related to the CDI scope in play? It's also possible that my inability to recreate really doesn't matter, ultimately, with any of the points discussed here.. (but thought it was worth mentioning).

@scottkurz
Copy link
Member

I've just tried to do this. In the reproducer that I shared, it seems to work. But in a larger project (which is also open source, but because it's so large I first created a small reproducer) it still doesn't work. As far as I can tell, all relevant Maven and OpenLiberty configuration is the same in both projects.

Thanks for responding. OK, fair enough. It just concerned me that we might be missing something without recreating that first step, but in any case, it's clear the loose app deployment for skinnyModules looks incorrect as noted. So since @tjwatson seemed to make the link from this incorrect structure to a potential classloader problem, I think we have enough detail to address this problem in the liberty-maven-plugin deploy code.

@arunvenmany-ibm arunvenmany-ibm linked a pull request Jan 22, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants