I want to set active profile host dependent for any envrionment and cannot find an environment independent hook.
Following factory will set the active profile before application context will build.
/META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=MyApplicationContextInitializer
MyApplicationContextInitializer.java
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext ca) {
ConfigurableEnvironment environment = ca.getEnvironment();
environment.addActiveProfile("myHost");
}
}
If this example is executed in a mock environment by JUnit ...
*Test.java
...
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
...
... following will be logged.
...
... RestControllerTests : The following profiles are active: myHost
...
but profile myHost is not active and default profile will be used in context of JUnit!
A test as Java Application and JUnit with VM arguments works ...
-Dspring.profiles.active=myHost
I use a war packaged spring-boot-starter-web app and programmatically profile shall be set and used in any environment
Java Application
JUnit
Servlet Container
How do I set the profile programmatically for any environmnet?
I do not want to use VM arguments or environemt variables because the profile shall be set by the current host name.
Simplest answer after a lot of googling :)
#SpringBootApplication
public class ExampleMain2 {
public static void main(String[] args) {
ConfigurableEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles("dev");
SpringApplication sa = new SpringApplication(ExampleMain2.class);
sa.setEnvironment(environment);
sa.setAdditionalProfiles("remote","live");
sa.run(args);
}
}
I had the same issue and I finally solved it implementing the ActiveProfilesResolver interface.
In your case you could do something like this:
public class MyActivateProfilesResolver implements ActiveProfilesResolver {
#Override
public String[] resolve(Class<?> testClass) {
// some code to find out your active profiles
return new String[] {"myHost"};
}
}
And then you need to link your test with your resolver like this:
#ActiveProfiles(resolver = MyActivateProfilesResolver.class)
I don't think there is such a thing as setting active profile dynamically (programmatically) at runtime once the application is running, any modification to the profile will not have any effect on the loaded beans and properties files.
However, before running the application you can configure the available profiles of the application. for example:
#SpringBootApplication
public class DemoApplicationWithSysProperty {
public static void main(String[] args) {
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME,
PROFILE_NAME);
SpringApplication.run(DemoApplicationWithSysProperty.class, args);
}
}
Don't you think that could be achieved with an EnvironmentPostProcessor ans still have an impact on the running environment? My attempts failed.
#Order(Ordered.LOWEST_PRECEDENCE)
public class EnvtPostProcessor implements EnvironmentPostProcessor {
#SuppressWarnings("deprecation")
#Override
public void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) {
if ((env.acceptsProfiles("bar1") || env.acceptsProfiles("bar2"))
&& !(env.acceptsProfiles("foo1") || env.acceptsProfiles("foo2"))) {
env.addActiveProfile("foo1");
}
}
}
NB: don't forget to register the processor in src/main/resources/META-INF/spring.factories:
org.springframework.boot.env.EnvironmentPostProcessor=com.mygroup.myapp.EnvtPostProcessor
When I only apply the "bar2" active profile, the "foo1" profile is added to the environnement as an active profile, before application context initialization; but not all beans are found eventually (as they are in the originally complete active profiles list use case).
Maybe that's because of the dependencies (Azure for Spring Cloud connection, by the way) : I end up with no OAuth2UserService found for my ActiveDirectoryConfigurer.
Related
It would be even possible to do this?
I need to deploy a spring cloud config server with property files inside the container, so I need to use the native profile, BUT in the company only allows certain profile names when running the applications.
There is a way to do some manual configuration to make a custom profile behaves like the native one?
Thanks!
Thanks #spencergibb !
This just works:
#EnableConfigServer
#SpringBootApplication
public class BrokernetConfigApplication {
public static void main(String[] args) {
SpringApplication.run(BrokernetConfigApplication.class, args);
}
#Bean
public NativeEnvironmentRepository nativeEnvironmentRepository(
NativeEnvironmentRepositoryFactory factory,
NativeEnvironmentProperties environmentProperties) {
return factory.build(environmentProperties);
}
}
In the Quarkus Application Configuration Guide it mentions how to configure an app with profiles (eg. %dev.quarkus.http.port=8181).
But is there a way to access a Profile (or Environment) API so I can log the active profiles ? For example something like Spring:
#ApplicationScoped
public class ApplicationLifeCycle {
private static final Logger LOGGER = LoggerFactory.getLogger("ApplicationLifeCycle");
#Inject Environment env;
void onStart(#Observes StartupEvent ev) {
LOGGER.info("The application is starting with profiles " + env.getActiveProfiles());
}
ProfileManager.getActiveProfile()?
I'm writing unit tests and trying to use ExecutionCondition for enabling the test only when specific profile activated exclusively.
I created my ExecutionCondition.
class EnabledWithH2ExclusiveExecutionCondition implements ExecutionCondition {
#Override
public ConditionEvaluationResult evaluateExecutionCondition(
final ExtensionContext context) {
// check the environment
}
#Autowired
private Environment environment;
}
But the environment is not injected.
How can I do that?
Because your ExecutionCondition is created by JUnit5 itself using reflection .It is not managed by Spring and so the #Autowired will not work.
You can call SpringExtension.getApplicationContext() to get Spring Context and then get the Environment from it :
#Override
public ConditionEvaluationResult evaluateExecutionCondition(final ExtensionContext context){
Environment env = SpringExtension.getApplicationContext(context).getEnvironment();
// check the environment
}
I have my standard Spring Boot application working. I have situations where I want to run a "job" which is basically some specific method normally run via a user doing something in their browser but I want to run it from command line.
I'm able to run an arbitrary class with gradlew;
./gradlew -PmainClass=kcentral.backingservices.URLMetaExtractor execute
However when run this way none of the "autowiring" works. What is a better way to execute an arbitrary class (that has a main method) such that it also works with any Autowiring?
EDIT:
I got some advice to use a CommandLineRunner and some args, which work to execute the command via:
./gradlew bootRun -Pargs=--reloadTestData
However, the Autowiring of my Repo is failing. What I have is:
#EnableAutoConfiguration
#EnableMongoAuditing
#EnableMongoRepositories(basePackageClasses=KCItemRepo.class)
#ComponentScan(basePackages = {"kcentral"})
public class ReloadTestData implements CommandLineRunner {
#Autowired
AddItemService addItemService;
#Autowired
KCItemRepo itemRepo;
#Autowired
KCItemRatingRepo itemRatingRepo;
private static final Logger log = LoggerFactory.getLogger(ReloadTestData.class);
public void reloadData(){
log.info("reloadData and called");
if (itemRepo == null){
log.error("Repo not found");
return;
}
long c = itemRepo.count();
log.warn("REMOVING ALL items "+c);
itemRepo.deleteAll();
log.warn("REMOVING ALL ratings");
itemRatingRepo.deleteAll();
}
itemRepo is always null even though I wire the same way in my 'regular' spring boot app without an issue. What do I need to do to have it wire properly?
The fact that you say you want to run a "job" suggests that you might want to use a scheduled task within your application, rather than trying to run it through the command line. e.g. Scheduling tasks in Spring
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
If you want to make a command line application work with Autowiring, you can make a command line application by making your Application class implement the CommandLineRunner interface, e.g. Spring Boot Console App
#SpringBootApplication
public class SpringBootConsoleApplication
implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
#Override
public void run(String... args) {
}
}
And add spring.main.web-application-type=NONE to the properties file.
If you want to stop the application after running you can use SpringApplication.exit(ctx). Don't know about your auto-wiring problem though, maybe try printing out the list of available beans which might give some insight. Example:
#Component
public class DoThenQuit implements CommandLineRunner {
#Autowired
private ApplicationContext ctx;
#Override
public void run(String[] args) {
// do some other stuff before quitting
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.stream(beanNames).forEach(System.out::println);
// then quit the application
SpringApplication.exit(ctx);
}
}
I've put an #IntegrationTest annotation on every test, and sometimes I use it to add properties to the environment. When running all tests together, it seems that only the properties encountered in the first #IntegrationTest annotation are used, so some tests are failing. Is there a way to force a reload of those properties?
This is an example of my usage:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes=TestApplication.class)
#WebAppConfiguration
#IntegrationTest("some.property=true")
public class SomeIntegrationTest {
Spring Boot Applications under test are started just once for all tests, which is a good thing regarding test performance. If you want to start another application with a different property set, you have to write another Spring Boot application class like this:
#Configuration
#EnableAutoConfiguration
public class MetricsTestApplication {
public static void main(String[] args) {
SpringApplication.run(MetricsTestApplication.class, args);
}
}
In the integration test, you reference that other class. In addition you have to set a different port than the first application has, and you can add the properties that differ from the properties of the first application:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes=MetricsTestApplication.class)
#WebAppConfiguration
#IntegrationTest({"server.port=8091","batch.metrics.enabled=true"})
Our MetricsTestApplication with the different property set is now started under port 8091.
According to the API documentation #IntegrationTest annotation is "signifying that the tests are integration tests (and therefore require an application to startup "fully loaded" and listening on its normal ports)".
If you want to use reloadable properties, you should use EnvironmentTestUtils.
eg.
#Autowired
Environment env;
#Autowired
ConfigurableApplicationContext ctx;
#Before
public void before() {
EnvironmentTestUtils.addEnvironment(ctx, "test.value:myValue");
}
#Test
public void testGreeting() {
assertThat(env.getProperty("test.value"), comparesEqualTo("myValue"));
}