rewrite-spring icon indicating copy to clipboard operation
rewrite-spring copied to clipboard

Problem with HibernateJpaConfiguration after migrating from com.sun.xml.bind:jaxb-impl to org.glassfish.jaxb:jaxb-runtime

Open rcsilva83 opened this issue 1 year ago • 4 comments

What version of OpenRewrite are you using?

I am using

  • Maven plugin v5.42.2
  • rewrite-spring v5.21.0

How are you running OpenRewrite?

I am using the Maven plugin, and my project is a single module project.

<plugin>
        <groupId>org.openrewrite.maven</groupId>
        <artifactId>rewrite-maven-plugin</artifactId>
        <version>5.42.2</version>
        <configuration>
          <activeRecipes>
            <recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3</recipe>
          </activeRecipes>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>org.openrewrite.recipe</groupId>
            <artifactId>rewrite-spring</artifactId>
            <version>5.21.0</version>
          </dependency>
        </dependencies>
      </plugin>

What is the smallest, simplest way to reproduce the problem?

    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.3.1</version>
    </dependency>

What did you expect to see?

No change.

What did you see instead?

    <dependency>
      <groupId>org.glassfish.jaxb</groupId>
      <artifactId>jaxb-runtime</artifactId>
    </dependency>

What is the full stack trace of any errors you encountered?

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: javax/xml/bind/annotation/XmlElement
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1806)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:954)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352)
	at my.company.app.App.main(App.java:14)
Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlElement
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:137)
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:124)
	at com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule.setupModule(JaxbAnnotationModule.java:98)
	at com.fasterxml.jackson.databind.ObjectMapper.registerModule(ObjectMapper.java:908)
	at com.fasterxml.jackson.databind.ObjectMapper.registerModules(ObjectMapper.java:1110)
	at com.fasterxml.jackson.databind.ObjectMapper.findAndRegisterModules(ObjectMapper.java:1194)
	at org.hibernate.type.format.jackson.JacksonJsonFormatMapper.<init>(JacksonJsonFormatMapper.java:27)
	at org.hibernate.type.format.jackson.JacksonIntegration.<clinit>(JacksonIntegration.java:18)
	at org.hibernate.boot.internal.SessionFactoryOptionsBuilder.lambda$determineJsonFormatMapper$7(SessionFactoryOptionsBuilder.java:833)
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveStrategy(StrategySelectorImpl.java:220)
	at org.hibernate.boot.registry.selector.internal.StrategySelectorImpl.resolveDefaultableStrategy(StrategySelectorImpl.java:180)
	at org.hibernate.boot.internal.SessionFactoryOptionsBuilder.determineJsonFormatMapper(SessionFactoryOptionsBuilder.java:829)
	at org.hibernate.boot.internal.SessionFactoryOptionsBuilder.<init>(SessionFactoryOptionsBuilder.java:313)
	at org.hibernate.boot.internal.SessionFactoryBuilderImpl.<init>(SessionFactoryBuilderImpl.java:50)
	at org.hibernate.boot.internal.DefaultSessionFactoryBuilderService.createSessionFactoryBuilder(DefaultSessionFactoryBuilderService.java:26)
	at org.hibernate.boot.internal.MetadataImpl.getSessionFactoryBuilder(MetadataImpl.java:169)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1502)
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:390)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:366)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802)
	... 15 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlElement
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
	... 39 common frames omitted

Are you interested in contributing a fix to OpenRewrite?

Yes

rcsilva83 avatar Oct 16 '24 19:10 rcsilva83

Hi @rcsilva83 , welcome back! The change made here might be spread across multiple repositories: https://github.com/openrewrite/rewrite-hibernate https://github.com/openrewrite/rewrite-migrate-java https://github.com/openrewrite/rewrite-spring

We'd need to figure out if there's a missing parallel change to be made in rewrite-hibernate, or whether there's more to the one to one replacement that's done currently. Any help appreciated as I lack a bit of detail on which version of Hibernate you're using, as the HibernateJpaConfiguration class for v3.3.x itself does not refer to javax/xml/bind/annotation/XmlElement, but does refer to javax.sql.DataSource also referenced in

  • https://github.com/spring-projects/spring-boot/issues/42178

timtebeek avatar Oct 16 '24 20:10 timtebeek

Hi, @timtebeek !

I tracked this problem and it isn't related to HibernateJpaConfiguration but to Jackson. We use some Jackson annotations on some @Entity classes, so the error occurred on 'entityManagerFactory' bean initialization. We use jackson-module-jaxb-annotations package that depends on javax.xml.bind classes... So we had the javax.xml.bind:jaxb-api dependency declared, as we are using Java 11/17.

The problem is that, after running the UpgradeSpringBoot_3_3 recipe, javax.xml.bind:jaxb-api as replaced by jakarta.xml.bind:jakarta.xml.bind-api which is managed para Spring Boot to version 4.0.2. This version doesn't provide javax.xml.bind classes.

I found some recipes that do this change:

  • org.openrewrite.java.migrate.javax.AddJaxbDependencies which is called by org.openrewrite.java.migrate.Java8toJava11 called by org.openrewrite.java.migrate.UpgradeToJava17 called by org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0
  • org.openrewrite.java.migrate.jakarta.JavaxXmlBindMigrationToJakartaXmlBind which is called by org.openrewrite.java.migrate.jakarta.JavaxMigrationToJakarta called by org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0

Given that, is there any way to tell OpenRewrite not to run that recipes or to ignore that dependency and leave it as it is?

rcsilva83 avatar Oct 21 '24 15:10 rcsilva83

Thanks for diving in! The most convenient way to customizing an existing composite recipe is through the recipe builder at https://app.moderne.io/. We're working on getting that integrated into docs.openrewrite.org now that it's using Docusaurus, but that might take a while longer.

That said; looking at the changes I wonder if instead of removing recipes we should be adding a migration step instead:

  • https://github.com/FasterXML/jackson-module-jaxb-annotations
  • https://github.com/FasterXML/jackson-modules-base/tree/2.19/jakarta-xmlbind

With a change dependency to go from jackson-module-jaxb-annotations to jackson-module-jakarta-xmlbind-annotations and a change type to go from JaxbAnnotationModule to JakartaXmlBindAnnotationModule. What are your thoughts on adding those?

timtebeek avatar Oct 21 '24 20:10 timtebeek

Actually, these dependencies are being pulled by external packages we depend on and they aren't update yet, unfortunately... I think, for my use case, it is better to make a new recipe using UpgradeSpringBoot_3_3 and then add javax.xml.bind:jaxb-api dependency. This way, we wouldn't need to maintain a custom UpgradeSpringBoot_3_3 recipe and having to manually update it, which seams a lot of work...

On the long term, don't you think this kind of change is a bit intrusive to make? I think situations like this, where the developer don't have full control on the migration to jakarta packages, may be quite common. What do you think?

In this case, I see 2 solutions:

  1. modify org.openrewrite.java.migrate.javax.AddJaxbDependencies and org.openrewrite.java.migrate.jakarta.JavaxXmlBindMigrationToJakartaXmlBind to, instead of changing the dependencies, just add jakarta.xml.bind:jakarta.xml.bind-api;
  2. Create a way to bypass certain changes on OpenRewrite, with 2 options:
    1. define a <inactiveRecipes> tag
    2. adding an comment like <!-- OpenRewrite OFF --> around the dependency we don't want OpenRewrite change;

rcsilva83 avatar Oct 22 '24 13:10 rcsilva83