Skip to content

ConfigurationPropertiesBeanDefinitionValidator interferes with EclipseLink load-time weaving #20798

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

Closed
grimsa opened this issue Apr 1, 2020 · 3 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@grimsa
Copy link

grimsa commented Apr 1, 2020

After upgrading Spring Boot from 2.1 to 2.2 our app failed to start.

Previous working configuration:

  • Spring Boot: 2.1.12.RELEASE
  • Spring Framework: 5.1.13.RELEASE
  • EclipseLink: 2.7.6 (Latest)

New failing configuration:

  • Spring Boot: 2.2.4.RELEASE
  • Spring Framework: 5.2.3.RELEASE
  • EclipseLink: 2.7.6 (Latest)

The symptom - error similar to this:

***************************
APPLICATION FAILED TO START
***************************

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    com.company.core.domain.DomainSpecificEntityA._persistence_get_propertyB(DomainSpecificEntityA.java)

The following method did not exist:

    'void com.company.core.domain.DomainSpecificEntityA._persistence_checkFetched(java.lang.String)'

These methods are normally generated during dynamic weaving performed by EclipseLink (JPA implementation).
org.eclipse.persistence.internal.jpa.weaving.PersistenceWeaver is that class that performs this weaving.

This PersistenceWeaver is registered in org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo#addTransformer during container entity manager factory creation.

Comparing the list of classes that got instrumented beween Spring Boot 2.1 and 2.2 revealed that the list was shorter in version 2.2.

The difference was due to the fact that some of the JPA entity classes were already loaded by the classloader, earlier than PersistenceWeaver's class transformation was added to the classloader.

I tracked down the location where these classes were getting loaded first and the stack trace was like this:

> java.lang.Class#getDeclaredConstructors   // once this executes the entity class is available in current classloader, excluding it from later instrumentation
>   org.springframework.boot.context.properties.ConfigurationPropertiesBindConstructorProvider#findConstructorBindingAnnotatedConstructor
>   org.springframework.boot.context.properties.ConfigurationPropertiesBindConstructorProvider#getBindConstructor(org.springframework.boot.context.properties.bind.Bindable<?>, boolean)
>   org.springframework.boot.context.properties.ConfigurationPropertiesBean.BindMethod#forType
>   org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator#validate
>   ...

The bean that was getting validated was similar to this:

// This class is registered as a Spring Bean:
public class ProjectManagerImp implements ProjectManager { ... }

// This is our custom interface extending our another (more generic) interface
public interface ProjectManager extends Service<Project> { ... }

// This is JPA entity extending an abstract JPA MappedSuperclass.
// Both of these get loaded into the classloader, excluding them from further instrumentation
public class Project extends PersistentDomainObjectWithMetaData { ... }

Workaround

Removing org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator made our app start and work as expected. We simply added this to our configuration:

    @Bean
    public BeanDefinitionRegistryPostProcessor offendingValidatorRemovingBeanDefinitionRegistryPostProcessor() {
        return new BeanDefinitionRegistryPostProcessor() {
            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            }

            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
                registry.removeBeanDefinition("org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator");
            }
        };
    }

Additional info

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 1, 2020
@philwebb
Copy link
Member

@grimsa Are you able to provide a small sample application that reproduces the problem?

@philwebb philwebb added the status: waiting-for-feedback We need additional information before we can continue label Apr 16, 2020
@grimsa
Copy link
Author

grimsa commented Apr 18, 2020

@philwebb Here you go: https://github.com/grimsa/spring-boot-20798-sample
I documented my findings in README.md in that repo, hope they help.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Apr 18, 2020
@wilkinsona
Copy link
Member

Thanks for the sample, @grisma, and for your patience while we found some time to investigate. I've managed to reproduce the problem.

In a nutshell, it isn't safe for a BeanFactoryPostProcessor to load classes or reflect over them when a load-time weaver exists. As you have observed, any class loading that the post-processor performs may then prevent subsequent instrumentation. There are a couple of ways that we could avoid the problem. We could just skip the validation if the context contains a loadTimeWeaver bean. Alternatively, we could defer the validation until the load-time weaving infrastructure is in place. There's no good way to detect this. We'd have to implement LoadTimeWeaverAware and BeanFactoryAware. LoadTimeWeaverAware beans are created early so when the bean is created and setBeanFactory is called, validation could be performed.

Flagging for team attention to see if we can come up with a more elegant solution.

@wilkinsona wilkinsona added type: bug A general bug and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged labels Jul 9, 2020
@wilkinsona wilkinsona added this to the 2.2.x milestone Jul 9, 2020
@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Jul 9, 2020
@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label Jul 13, 2020
@wilkinsona wilkinsona modified the milestones: 2.2.x, 2.2.9 Jul 14, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants