Affects: \<6.0.4>

When upgrading from spring-core-5.3.24.jar to spring-core-6.0.4.jar (spring-boot 2 to 3 upgrade)

Our test uses persistence.xml descriptions for persistence units. The test fails for a duplicated persistence unit.

main cause found in debugger: the set created at org.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources line 325 with one element returned by findAllModulePathResources(locationPatternWithoutPrefix) :

  • a org.springframework.core.io.FileSystemResource whose Path (its hash source material) is /C:/SomePath/target/test-classes/META-INF/persistence.xml

then the set is completed line 332 by the result of findAllClassPathResources(locationPatternWithoutPrefix), which returns the same resource as the old version (spring-core-5.3.24):

  • an org.springframework.core.io.UrlResource whose getCleanedUrl() (its hash source material) is file:/C:/SomePath/target/test-classes/META-INF/persistence.xml (notice the file: prefix)

The found resources are the same file resource in 2 different Java structures, but their hash being solved differently, they both are added to the set, and therefore this resource conflicts with itself.

Consequence: the method org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager#preparePersistenceUnitInfos fails the test at line 470 because the persistence units are duplicated (but they come from the same source).

Comment From: Del-Tab

I see several ways to fix this, I think the one having less risk of consequences is cleaning the set before returning it in the PathMatchingResourcePatternResolver::getResources(String) method. But there may be more clever ways to deal with it.

If I've time (maybe next weekend) I'll write a pull request for you to decide but it looks like an easy fix.

Comment From: jhoeller

We have some measures in place for the automatic detection of duplicates already, in particular through conversion to FileSystemResource instead of UrlResource with a file protocol wherever possible. However, this is currently limited to our pattern matching algorithm; we seem to have missed the straight full-path case in findAllClassPathResources. As far as I see, we need to make convertClassLoaderURL perform such eager FileSystemResource conversion to cover that as well.

So thanks for the report, I'll sort this out today. Maybe you could test a 6.0.5 snapshot before the release next week then, double-checking that it works in your scenario.

Comment From: Del-Tab

As far as I see, we need to make convertClassLoaderURL perform such eager FileSystemResource conversion to cover that as well.

that's what I planned to think about in my push request :) I'll let you fix this then :)

Maybe you could test a 6.0.5 snapshot before the release next week then, double-checking that it works in your scenario.

Unfortunately I already switched to an annotation definition for all our persistence definition, since we have an internal delivery soon and I couldn't wait (I'm working for a client so I can't really share the code for testing) But I suppose a simple persistence.xml with a single persistentUnit object mapped shall trigger the issue.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="3.0"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">

    <persistence-unit name="my-persist-unit">
        <class>some.package.domains.SomeEntity</class>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

and this in a class annoted @Configuration

@Bean(name="entityManagerFactory")
public AbstractEntityManagerFactoryBean entityManagerFactoryBean(DataSource datasource) {
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setDataSource(datasource);
    factory.setPersistenceUnitName("my-persist-unit");
    factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    return factory;
}

and the class was annoted in a DataJpaTest class

Comment From: JoeyBling

The creation of an org.springframework.core.io.FileSystemResource instance in PathMatchingResourcePatternResolver#convertClassLoaderURL does not deal with special characters(Like Chinese or spaces, etc).

Maybe it should be solved as follows, buy not directly calling java.net.URL#getPath.

protected Resource convertClassLoaderURL(URL url) {
    return (ResourceUtils.URL_PROTOCOL_FILE.equals(url.getProtocol()) ?
            new FileSystemResource(ResourceUtils.getFile(url)) : new UrlResource(url));
}

Similar problems:

  • https://github.com/spring-projects/spring-boot/issues/34379
  • https://github.com/spring-projects/spring-boot/issues/34364
  • 30031