Skip to content
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

Spring ACL and native compilation fail to process datasource properties #12653

Closed
RutgerLubbers opened this issue Feb 9, 2023 · 8 comments
Closed
Assignees
Labels
in: config An issue in spring-security-config type: bug A general bug
Milestone

Comments

@RutgerLubbers
Copy link

Using Spring Boot 3, Spring 6, Java 17 and gradle, when performing a nativeCompile, the compilation fails in the processAot task:

...
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception with message: Failed to determine suitable jdbc url
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
        ... 87 more
...

It seems that the @ConfigurationProperties(prefix = "spring.datasource") is not processed in this case. Typically, when h2 is added to the classpath, then the error does not occur (no other changes required), however I suspect that the postgresql classes will not be present in the native build.

I would've expected the datasource to be present, so the JDBC connection can be used in the native image.

I've created a sample to see this behaviour, see https://github.com/RutgerLubbers/native-acl-jdbc

@RutgerLubbers RutgerLubbers added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Feb 9, 2023
@marcusdacoregio
Copy link
Contributor

Hi @RutgerLubbers, since that is a Spring Boot application, can't you let Spring Boot create the DataSource for you instead?

@marcusdacoregio marcusdacoregio self-assigned this Jul 6, 2023
@marcusdacoregio marcusdacoregio added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 6, 2023
@RutgerLubbers
Copy link
Author

RutgerLubbers commented Jul 13, 2023

Hi @marcusdacoregio ,

If native compilation is disabled, then ./gradlew bootRun works.
If native compilation is enabled, then it does not seem to get a proper datasource during AOT compilation.

In the example the datasource is defined in the application.yaml.
There is a com.ilionx.poc.DataSourceConfig which is just for debugging puposes, its presence does not change the outcome in any manner.

The example does a native compilation which results in an error, so doing a ./gradlew bootRun fails.
If the org.graalvm.buildtools.native plugin were to be removed from the build.gradle.kts, then the ./gradlew bootRun works (though not natively).

Adding the h2 database to the dependencies, native compilation does work. However the application does connect to the postgresl database afterwards. Adding a "random" in-memory database to the application is not something I want to do, however it is a workaround (at best).

So, the issue is, why does native compilation fail (without H2)?

(Where I say "bootRun", this also goes for "compileNative", only native compilation without the native compilation plugin fails ;-))

@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 Jul 13, 2023
@wilkinsona
Copy link
Member

Generally speaking, beans shouldn't be created during AOT processing as it's only bean definitions that are important. There are a few exceptions, for example BeanFactoryPostProcessors are created. Beans related to proxying will also be created as proxy creation has to be performed at build time. It's proxy creation that appears to be the cause of the problem here:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'preFilterAuthorizationMethodInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.class]: Failed to instantiate [org.springframework.aop.Advisor]: Factory method 'preFilterAuthorizationMethodInterceptor' threw exception with message: Error creating bean with name 'methodSecurityExpressionHandler' defined in class path resource [com/ilionx/poc/SpringSecurityAclConfig.class]: Unsatisfied dependency expressed through method 'methodSecurityExpressionHandler' parameter 0: Error creating bean with name 'aclPermissionEvaluator' defined in class path resource [com/ilionx/poc/SpringSecurityAclConfig.class]: Unsatisfied dependency expressed through method 'aclPermissionEvaluator' parameter 0: Error creating bean with name 'mutableAclService' defined in class path resource [com/ilionx/poc/SpringSecurityAclConfig.class]: Unsatisfied dependency expressed through method 'mutableAclService' parameter 1: Error creating bean with name 'basicLookupStrategy' defined in class path resource [com/ilionx/poc/SpringSecurityAclConfig.class]: Unsatisfied dependency expressed through method 'basicLookupStrategy' parameter 0: Error creating bean with name 'dataSource' defined in class path resource [com/ilionx/poc/DataSourceConfig.class]: Failed to instantiate [javax.sql.DataSource]: Factory method 'dataSource' threw exception with message: Failed to determine a suitable driver class
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:659)
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:647)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
        at org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans(BeanFactoryAdvisorRetrievalHelper.java:91)
        at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findCandidateAdvisors(AbstractAdvisorAutoProxyCreator.java:111)
        at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:96)
        at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:78)
        at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.determineBeanType(AbstractAutoProxyCreator.java:249)
        at org.springframework.context.support.GenericApplicationContext.preDetermineBeanTypes(GenericApplicationContext.java:431)
        at org.springframework.context.support.GenericApplicationContext.refreshForAotProcessing(GenericApplicationContext.java:413)
        at org.springframework.context.aot.ApplicationContextAotGenerator.lambda$processAheadOfTime$0(ApplicationContextAotGenerator.java:54)
        at org.springframework.context.aot.ApplicationContextAotGenerator.withCglibClassHandler(ApplicationContextAotGenerator.java:67)
        at org.springframework.context.aot.ApplicationContextAotGenerator.processAheadOfTime(ApplicationContextAotGenerator.java:53)
        at org.springframework.context.aot.ContextAotProcessor.performAotProcessing(ContextAotProcessor.java:106)
        at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:84)
        at org.springframework.context.aot.ContextAotProcessor.doProcess(ContextAotProcessor.java:49)
        at org.springframework.context.aot.AbstractAotProcessor.process(AbstractAotProcessor.java:82)
        at org.springframework.boot.SpringApplicationAotProcessor.main(SpringApplicationAotProcessor.java:80)

The link between preFilterAuthorizationMethodInterceptor and the DataSource needs to be broken somehow. Ideally that would happen as close as possible to preFilterAuthorizationMethodInterceptor so that the number of beans created during AOT processing is kept to a minimum.

@marcusdacoregio marcusdacoregio added in: acl An issue in spring-security-acl and removed status: feedback-provided Feedback has been provided labels Jul 13, 2023
@marcusdacoregio
Copy link
Contributor

Hi @RutgerLubbers, thanks for the report.

I tried the latest version of your repository and it seems that the error is not happening in Spring Boot 3.1.1.
Can you confirm if it was fixed or if the steps to reproduce changed?

@marcusdacoregio marcusdacoregio added the status: waiting-for-feedback We need additional information before we can continue label Jul 31, 2023
@spring-projects-issues
Copy link

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label Aug 7, 2023
@RutgerLubbers
Copy link
Author

Hi @marcusdacoregio , sorry, I left the sample in a state where it did work (it included the H2 database), so building did not trigger the error.

I've updated the build file to include all latest versions and the sample now triggers the erroneous behaviour.
It is now on Spring Boot 3.1.2.

@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 status: feedback-reminder We've sent a reminder that we need additional information before we can continue labels Aug 8, 2023
@marcusdacoregio marcusdacoregio removed the status: feedback-provided Feedback has been provided label Sep 12, 2023
@marcusdacoregio marcusdacoregio added this to the 6.0.7 milestone Sep 12, 2023
@marcusdacoregio
Copy link
Contributor

Hi, @RutgerLubbers. Sorry for the delay on this.

The reason why it works when adding the H2 dependency is because the DataSourceProperties tries to resolve the driver class name from the embedded database connection if there is no spring.datasource.driver-class-name property, therefore, the DataSource bean can be created at build-time. When you remove the dependency, the driver cannot be resolved anymore and the error is thrown.

Based on @wilkinsona comment, I think the best option here is to defer the initialization of the MethodSecurityExpressionHandler, similar to what is done with DeferringObservationAuthorizationManager.

I'll reach out to @jzheaux to make sure that there are no obvious side-effects on doing that and then I'll push a fix.

marcusdacoregio added a commit to marcusdacoregio/spring-security that referenced this issue Sep 13, 2023
When using Spring Security ACL and compiling to Native, in order to create the '*AuthorizationMethodInterceptor' Proxy beans during build time, Spring tries to resolve the DataSource bean since the DataSource can be a dependency of some AclService implementations, and fails because some required data source properties are not available during build time.

This commit defers the initialization of the MethodSecurityExpressionHandler to the runtime.

Closes spring-projectsgh-12653
@marcusdacoregio marcusdacoregio added in: config An issue in spring-security-config and removed in: acl An issue in spring-security-acl labels Sep 14, 2023
@RutgerLubbers
Copy link
Author

Thanks Marcus, Andy! (and others?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config type: bug A general bug
Projects
Status: No status
Development

No branches or pull requests

4 participants