Assuming a BeanFactoryInitializationAotProcessor would like to register a resource that's not dependent on the ConfigurableListableBeanFactory handed into the method but globally applicable. It would return a BeanFactoryInitializationAotContribution that'd do something like this with the GenerationContext provided:
context.getGeneratedFiles().addResourceFile("someLocation", "someContent");
This fails for a subsequent run of the AOT process if the build hasn't cleaned out someLocation beforehand, as the resource creation rejects overwriting files by default. To reliably always create or replace the file to be created, one has to resort to this:
context.getGeneratedFiles().handleFile(Kind.RESOURCE, "someLocation", it -> {
var resource = new ByteArrayResource("someContent".getBytes(StandardCharsets.UTF_8));
if (it.exists()) {
it.override(resource);
} else {
it.create(resource);
}
});
This is pretty involved compared to the simple call to ….addResourceFile(…). I wonder if it makes sense to add method overloads that allow triggering the always-override behavior. Or, alternatively, flip the default to allow the override and rely on users with more advanced needs to simply use the already existing API that gives them more control over the file creation.
Related tickets
- spring-projects/spring-modulith#1457
Comment From: snicoll
This fails for a subsequent run of the AOT process if the build hasn't cleaned out someLocation beforehand
You are misunderstanding the problem. The problem occurs if several application contexts are handled within a single invocation of the AOT process. The OP in the linked issue hinted at that with "As soon as I set the Spring Actuator port to a different port". This will effectively trigger a separate context for the Actuator and you end up in the problem that the contribution is called again for the second context.
You also have, as we've discussed, this situation almost all the time for processing tests as they rarely operate on a single ApplicationContext arrangement.
flip the default to allow the override and rely on users with more advanced needs to simply use the already existing API that gives them more control over the file creation.
The problem with that suggestion is that it hides the problem to those generating a file that does change based on the state of the application context. It would also be inconsistent with what happens with source code (If you try to add a class that already exists, it should fail). I don't know if it's more obvious that classes can't be overridden but resources can.
Wondering what @philwebb thinks since he's been working on the initial implementation.
Comment From: odrotbohm
I completely understand that extra care has to be taken in case there is a relationship between the config file and the ApplicationContext the AOT process is running for. I'd just argue that if that's the case, the implementor knows about that and wouldn't use the simple addResourceFile(…) methods anyway.
To quote myself:
… a resource that's not dependent on the ConfigurableListableBeanFactory handed into the method but globally applicable.
What I tried to express with the statement is that such a relationship does not exist in my case and that it would be nice if the simpler case (no relationship) would not require the more complex arrangement.
If you think that the more complex case is the common one, what would be the use case for the simple registration methods? In other words: in which case would one ever call addResourceFile(…) at all? Because even for unique (per-AC) file names, a second run of the AOT plugin would fail, wouldn't it?
Comment From: snicoll
I completely understand that extra care has to be taken in case there is a relationship between the config file and the ApplicationContext the AOT process is running for.
At this time, this is the default. We may or may not agree that's a good default and that's why I asked feedback from Phil.
I'd just argue that if that's the case, the implementor knows about that
That's not how this works. You should not care when you create a class and that's what's done by default. The main problem here is that we don't do that for resources and we should. I've opened https://github.com/spring-projects/spring-framework/issues/35862.
As for the rest of the comment, let's stick with what the default is currently. If we decide to change it, it'll auto-fix the problem you are reporting but I don't think it is as easy as it sounds. Certainly not without https://github.com/spring-projects/spring-framework/issues/35862 implemented. That being said, I agree things are broken at the moment.
Something else that's interesting is the concept of an "Application" contribution that would be more explicit than just adding a file.