Hi,

I was playing with spring.xml.ignore and found an NPE when loading CharSequence sources.

The following application snippet should reproduce the error

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.setSources(Set.of("test", "source"));
        app.run(args);
    }

}

When running java -Dspring.xml.ignore=true -jar app.jar the following NPE occurs:

java.lang.NullPointerException: null
        at org.springframework.boot.BeanDefinitionLoader.load(BeanDefinitionLoader.java:192)
        at org.springframework.boot.BeanDefinitionLoader.load(BeanDefinitionLoader.java:155)
        at org.springframework.boot.BeanDefinitionLoader.load(BeanDefinitionLoader.java:136)
        at org.springframework.boot.SpringApplication.load(SpringApplication.java:691)
        at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:392)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)

This is due to the fact that xmlReader is null when XML support is disabled. As the reader is only used to get the Environment, I think we can workaround this by just using the scanner field. They should have the same Environment here.

Unfortunately, it's a bit cumbersome to write a test for this. I would need to set the final xmlReader field to null via reflection, which is what I try to avoid usually. Nonetheless, let me know If I should add a test like described that mimics XML support being disabled.

Cheers, Christoph

P.S.: I tried setting spring.xml.ignore in my application.properties first, which didn't work. I think we would need to use Binder facilities to make this work. Maybe that's worth evaluating.

Comment From: wilkinsona

Unfortunately, it's a bit cumbersome to write a test for this.

We talked a bit about this last week. We're increasingly uncomfortable with these Graal-specific changes that are hard to test. Our assumption had been that any problems would be detected by the Spring Graal Native project but it appears to have been missed there too.

@sdeleuze I think we need to revisit our approach here. We either need to figure how how to test these changes in Boot (without relying on Graal) or we need a commitment on your side to test things.

Comment From: wilkinsona

With a change to make ModifiedClasspathExtension public, I think we can test this with the following:

package org.springframework.boot;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.boot.testsupport.classpath.ModifiedClassPathExtension;
import org.springframework.context.support.StaticApplicationContext;

import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

@ExtendWith(ModifiedClassPathExtension.class)
class IgnoringXmlBeanDefinitionLoaderTests {

    @BeforeAll
    static void ignoreXml() {
        System.setProperty("spring.xml.ignore", "true");
    }

    @AfterAll
    static void enableXml() {
        System.clearProperty("spring.xml.ignore");
    }

    @Test
    void whenXmlSupportIsDisabledXmlSourcesAreRejected() {
        assertThatExceptionOfType(BeanDefinitionStoreException.class)
                .isThrownBy(() -> new BeanDefinitionLoader(new StaticApplicationContext(),
                        "classpath:org/springframework/boot/sample-beans.xml").load())
                .withMessage("Cannot load XML bean definitions when XML support is disabled");
    }

}

Comment From: wilkinsona

Thanks very much, @dreis2211.

Comment From: dreis2211

That's a neat trick to use ModifiedClassPathExtension, @wilkinsona . Given that I wrote this thing, I could have come up with that as well, I guess. ;-)

Comment From: dreis2211

I wonder if we should rather introduce a new annotation ForkedClassPath instead of making ModifiedClassPathExtension public btw.

Comment From: wilkinsona

I wonder if we should rather introduce a new annotation ForkedClassPath instead of making ModifiedClassPathExtension public btw.

I was in two minds about that, and I still am really. I don't have any objection to introducing a custom annotation. I guess we'd want to prevent @ForkedClassPath being used alongside @ClassPathExclusions or @ClassPathOverrides to make it clear that it is intended for the situation where you just need a "copy" of the class path rather than a modified variant of it.

Comment From: dreis2211

Mind if I experiment with that if I find the time?

Comment From: wilkinsona

Not at all. That'd be great!