Skip to content
This repository has been archived by the owner on Feb 23, 2023. It is now read-only.

Spring profile management #764

Closed
nfrankel opened this issue May 2, 2021 · 24 comments
Closed

Spring profile management #764

nfrankel opened this issue May 2, 2021 · 24 comments
Assignees
Labels
status: duplicate A duplicate of another issue type: enhancement A general enhancement

Comments

@nfrankel
Copy link

nfrankel commented May 2, 2021

I'm trying to "GraalVM-native"-ify the Spring PetClinic REST. I managed to fix expected configuration issues. However, I'm stuck now on Spring profiles.

The application offers several profiles: one set for data access and one for database. When I start the native image, I get the following:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.samples.petclinic.repository.PetRepository' available: expected single matching bean but found 3: jpaPetRepositoryImpl,jdbcPetRepositoryImpl,springDataPetRepository
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
	... 34 more

While the JVM will automatically select the bean that matches the correct profile, the Native Image cannot.

I tried with the default command line and by passing -Dspring.profiles.active=jdbc. The logs display respectively the default profile and the jdbc one but the problem is the same.

I didn't find anything related to profile management in the documentation.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 2, 2021
@sdeleuze
Copy link
Contributor

sdeleuze commented May 3, 2021

Hi Nicolas, could you please detail how do you pass -Dspring.profiles.active=jdbc in the native image variant? We can maybe improve the documentation indeed.

@sdeleuze sdeleuze added the status: waiting-for-feedback We need additional information before we can continue label May 3, 2021
@sdeleuze sdeleuze self-assigned this May 3, 2021
@nfrankel
Copy link
Author

nfrankel commented May 3, 2021

docker run -it --rm -p9966:9966 code-with:springboot -Dspring.profiles.active=jdbc

It seems as if at runtime, all candidate beans are part of the context and no profile-related filtering occurs.

@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 May 3, 2021
@sdeleuze
Copy link
Contributor

sdeleuze commented May 6, 2021

I don't think passing -Dspring.profiles.active=jdbc to docker is expected to work, a quick test with a JVM image shows it does not.

@sdeleuze
Copy link
Contributor

sdeleuze commented May 6, 2021

You can use docker run --rm -e spring_profiles_active=jdbc ... instead. That's not specific to Spring Native so I won't document it there.

@sdeleuze sdeleuze added type: question A question and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on labels May 6, 2021
@sdeleuze sdeleuze closed this as completed May 6, 2021
@sdeleuze sdeleuze reopened this May 6, 2021
@sdeleuze sdeleuze added status: waiting-for-triage An issue we've not yet triaged or decided on and removed type: question A question labels May 6, 2021
@sdeleuze
Copy link
Contributor

After an additional discussion with Nicolas, it seems indeed we need to refine profile management to work as expected.

@sdeleuze sdeleuze removed the status: waiting-for-triage An issue we've not yet triaged or decided on label May 11, 2021
@sdeleuze sdeleuze added this to the Backlog milestone May 11, 2021
@sdeleuze
Copy link
Contributor

I put it in the backlog for now, since I don't know how involved it is. We can move it to 0.1x as soon as somebody tackle this one.

@jdubois
Copy link

jdubois commented Jun 15, 2021

I've just done some tests with Spring Native 0.10.0 and I confirm that Spring profiles ( the org.springframework.context.annotation.Profile annotation to be exact) seem to be totally ignored.

I even tested using one bean with @Profile("azure") and another with @Profile("!azure") and I got the 2 available in my context! For me the annotation is simply ignored.

@sdeleuze
Copy link
Contributor

Let's re-evaluate that one after 0.11 which will change a lot of related things.

@aclement
Copy link
Contributor

aclement commented Jul 4, 2021

After digging into #888 - I think some of the problem here may simply be that there is no type hint for Profiles, so I've added it. Working on a test case at the mo.

@sdeleuze
Copy link
Contributor

sdeleuze commented Jul 6, 2021

@nfrankel Could you please do more tests with 0.10.2-SNAPSHOT?

@nfrankel
Copy link
Author

nfrankel commented Jul 6, 2021

Same

docker run -it --rm -p9966:9966 code-with:springboot -Dspring.profiles.active=jdbc,hsqldb
[main] INFO org.springframework.nativex.NativeListener - This application is bootstrapped with code generated with Spring AOT

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.5)

[main] INFO org.springframework.samples.petclinic.PetClinicApplication - Starting PetClinicApplication using Java 1.8.0_292 on dc023a6e1e60 with PID 1 (/workspace/org.springframework.samples.petclinic.PetClinicApplication started by cnb in /workspace)
[main] INFO org.springframework.samples.petclinic.PetClinicApplication - The following profiles are active: jdbc,hsqldb
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 6 ms. Found 7 JPA repository interfaces.
[main] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 9966 (http)
Jul 06, 2021 3:05:12 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Jul 06, 2021 3:05:12 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/9.0.45]
Jul 06, 2021 3:05:12 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring embedded WebApplicationContext
[main] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 115 ms
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[main] INFO com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Driver does not support get/set network timeout for connections. (feature not supported)
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
[main] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
[main] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'visitRestController': Unsatisfied dependency expressed through field 'clinicService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'clinicServiceImpl' defined in class path resource [org/springframework/samples/petclinic/service/ClinicServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.samples.petclinic.repository.PetRepository' available: expected single matching bean but found 3: jpaPetRepositoryImpl,jdbcPetRepositoryImpl,springDataPetRepository
[main] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
[main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
[main] INFO org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener - 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[main] ERROR org.springframework.boot.SpringApplication - Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'visitRestController': Unsatisfied dependency expressed through field 'clinicService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'clinicServiceImpl' defined in class path resource [org/springframework/samples/petclinic/service/ClinicServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.samples.petclinic.repository.PetRepository' available: expected single matching bean but found 3: jpaPetRepositoryImpl,jdbcPetRepositoryImpl,springDataPetRepository
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:782)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:774)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:339)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1340)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329)
        at org.springframework.samples.petclinic.PetClinicApplication.main(PetClinicApplication.java:10)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'clinicServiceImpl' defined in class path resource [org/springframework/samples/petclinic/service/ClinicServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.samples.petclinic.repository.PetRepository' available: expected single matching bean but found 3: jpaPetRepositoryImpl,jdbcPetRepositoryImpl,springDataPetRepository
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1354)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1380)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
        ... 21 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.samples.petclinic.repository.PetRepository' available: expected single matching bean but found 3: jpaPetRepositoryImpl,jdbcPetRepositoryImpl,springDataPetRepository
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
        ... 34 more

@aclement
Copy link
Contributor

aclement commented Jul 6, 2021

Alright, I can try specifically on that pet clinic rest sample. I've been unable to create a profile problem with my playing around (doesn't mean there isn't one, I just can't in a simple case). @nfrankel do you by any chance have a fork of how far you've gotten it?

@nfrankel
Copy link
Author

nfrankel commented Jul 6, 2021

Sure https://github.com/nfrankel/spring-petclinic-rest/tree/native

Thanks for your help

@sdeleuze sdeleuze assigned aclement and unassigned sdeleuze Jul 7, 2021
@aclement
Copy link
Contributor

aclement commented Jul 7, 2021

I actually took Profile.class out again of our hints because I couldn't create a test case that didn't infer it automatically. However, this project is one of those cases :) So, I have added it back.

Without Profile in a hint the app fails in the way described. With Profile, it doesn't fail with that, it fails with something else. I think it is missing a bunch of config for other things (spring fox, swagger) - so I used the agent to collect that missing config. I tweaked it a little but it is still more verbose than it needs to be, the config is here:
https://github.com/aclement/spring-petclinic-rest/tree/native/src/main/resources/META-INF/native-image/a/b

I also modified the pom.xml in my fork so I could build locally without the docker buildpack need. With that config I can run:

mvn -DskipTests -Pnative clean package

Now I will say the app doesn't work for me (I get an error page for http://localhost:9966/petclinic - in either JVM or the native-image), so I've just been debugging startup and the app now starts for me.

I tried passing the jdbc profile and the app fails but I just think it is more missing configuration. I didn't run the JVM app with that profile and the agent to collect extra config, that could be a next step.

But I think the fundamental issue here, that profiles were misbehaving, is fixed. The addition of Profile/ActiveProfiles to the hints was the answer.

@nfrankel
Copy link
Author

nfrankel commented Jul 8, 2021

But I think the fundamental issue here, that profiles were misbehaving, is fixed. The addition of Profile/ActiveProfiles to the hints was the answer.

I'm a bit confused: should I do something, or is it on your side?

@aclement
Copy link
Contributor

aclement commented Jul 8, 2021

No, you don't need to do anything. The hints for the Profile/ActiveProfiles are built in now (0.10.2 snapshots). Your app can start without the error in the first comment of this issue. However, the app has other issues because we don't have defined configuration for all those other libraries.

@nfrankel
Copy link
Author

nfrankel commented Jul 8, 2021

No, you don't need to do anything

Perfect

However, the app has other issues because we don't have defined configuration for all those other libraries.

Yes, I started to configure those libraries

Thanks for your help!

@nfrankel
Copy link
Author

I've just rebuilt the image using the latest snapshot:

mvnd -U clean spring-boot:build-image -DskipTests

I launch the application with profiles:

docker run -it --rm -p9966:9966 docker.io/library/code-with:springboot -Dspring.profiles.active=jdbc,hsqldb

I now have another kind of error:

[main] INFO org.springframework.nativex.NativeListener - This application is bootstrapped with code generated with Spring AOT

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.5)

[main] INFO org.springframework.samples.petclinic.PetClinicApplication - Starting PetClinicApplication using Java 1.8.0_292 on 92dc2ab6084f with PID 1 (/workspace/org.springframework.samples.petclinic.PetClinicApplication started by cnb in /workspace)
[main] INFO org.springframework.samples.petclinic.PetClinicApplication - The following profiles are active: jdbc,hsqldb
[main] WARN org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [org.springframework.samples.petclinic.PetClinicApplication]; nested exception is java.lang.IllegalArgumentException: Could not find class [org.springframework.context.annotation.ProfileCondition]
[main] INFO org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener - 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
[main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter - 

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

Description:

Native reflection configuration for org.springframework.context.annotation.ProfileCondition is missing.

Action:

Native configuration for a class accessed reflectively is likely missing.
You can try to configure native hints in order to specify it explicitly.
See https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#native-hints for more details.

It seems I lack the reflection configuration for a Spring class (ProfileCondition)

@aclement
Copy link
Contributor

As I recall, the app lacks many many different kinds of configuration (and when one is missing they won't all present in an obvious way like that). It'll be slow going unless you use the agent to collect them (are you using it?)

We could probably add ProfileCondition, but it'd be good to have a small test case for the usage. Are you just going to manually add it locally for now?

@nfrankel
Copy link
Author

nfrankel commented Sep 5, 2021

Sorry for the delay, I'm back on it.

I've added the @ProfileCondition manually and I confirm I can go further (and fail because other things break).

In all cases, I do believe that if you want to improve the developer experience, it should be included in the out-of-the-box configuration. There are two use-cases:

  1. Set the profile at compile-time and run the image with them
  2. Compile with all profiles and set them at runtime

@sdeleuze
Copy link
Contributor

Let's try to see if we can allow to set AOT profiles with maybe predefined ones for aot ant aot-test in 0.11.1.

@sdeleuze
Copy link
Contributor

sdeleuze commented Jan 10, 2022

I tend to think this issue duplicates #1411 which provide a documented way to use profiles with the AOT engine.

@sdeleuze sdeleuze added the status: duplicate A duplicate of another issue label Jan 10, 2022
@sdeleuze sdeleuze removed this from the 0.11.2 milestone Jan 10, 2022
@nfrankel
Copy link
Author

Nitpick: the link to the documented way is wrong.

@sdeleuze
Copy link
Contributor

Oups fixed thanks.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: duplicate A duplicate of another issue type: enhancement A general enhancement
Development

No branches or pull requests

5 participants