Property resolving for multiple Spring profiles (yaml configuration) - spring

Is there a defined order when multiple profiles are used for a property resolution.
I have yaml configuration file:
name: none
---
spring:
profiles: prod
name: prodName
---
spring:
profiles: dev
name: devName
When application runs with no (default) profile, none is printed. For dev/prod profiles devName/prodName is printed (so far so good).
When I tried to define profile as dev,prod prodName is printed,when I specify prod,dev devName is printed.
Is this something I can rely on? I mean is it specified in Spring? I didn't find it here.
full version (for replication)
application.yml
name: none
---
spring:
profiles: dev
name: devName
---
spring:
profiles: prod
name: prodName
Configuration.java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
#Component
#ConfigurationProperties
public class Configuration {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SpringBootConsoleApplication.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.List;
#SpringBootApplication
public class SpringBootConsoleApplication implements CommandLineRunner {
private static Logger LOG = LoggerFactory.getLogger(SpringBootConsoleApplication.class);
#Autowired
Configuration conf;
public static void main(String[] args) {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
#Override
public void run(String... args) {
LOG.info("name: {}", conf.name);
}
}
edit:
GitHub repository
sample output:
c:\betlista\SpringPropertyResolutionMultipleProfiles>java -jar target\spring-boot-console-app-1.0.jar
...: name: none, label: labelValue
c:\betlista\SpringPropertyResolutionMultipleProfiles>java -jar -Dspring.profiles.active=dev target\spring-boot-console-app-1.0.jar
...: name: devName, label: labelValue
c:\betlista\SpringPropertyResolutionMultipleProfiles>java -jar -Dspring.profiles.active=dev,prod target\spring-boot-console-app-1.0.jar
...: name: prodName, label: labelValue
c:\betlista\SpringPropertyResolutionMultipleProfiles>java -jar -Dspring.profiles.active=prod,dev target\spring-boot-console-app-1.0.jar
...: name: devName, label: labelValue
edit 2:
Topics
There was a question which I'd refer to as topics - Multiple properties file for a single spring profile
While one can use #PropertySource with property files, it cannot be used with YAML files. The only solution I know at the moment is to used e.g. -Dspring.config.additional-location=classpath:topic1.yml
edit 3:
The "duplicate" question has same questions as I have here, but without an answer. That question was about beans, not really a properties. Accepted answer says "spring.profiles.active system property doesn't matter.", which I shown as incorrect (within context of properties). Please vote for reopen if you agree.

Is this something I can rely on?
Yes, the last-win strategy is consistent and is documented in the reference guide:
If several profiles are specified, a last-wins strategy applies. For example, if profiles prod,live are specified by the spring.profiles.active property, values in application-prod.properties can be overridden by those in application-live.properties.
(Unrelated, but about the link to the doc you've shared. I don't know if that's an accident but that's the documentation for Spring Boot 1.2.x which was released over 6 years ago).

Related

Spring Boot Actuator to show service start datetime

I am using spring-boot-starter-actuator in my project wanted to show service starts date time along with other information on /info end point.
Please guide how to achieve this.
Thanks in advance
/startup - is the Actuator Endpoint to see the startup information.
Sample URL syntax:
http://<HOST>:<port>/actuator/startup
For more info, visit here
--- Edit---
/startup endpoint does not get exposed by default, Hence need to enable explicitly by below property in application.properties :
management.endpoints.web.exposure.include=startup
BufferingApplicationStartup class is in-memory buffered implementation for capturing startup steps. Hence in the main class below changes are required:
import java.util.TimeZone;
import javax.annotation.PostConstruct;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
#SpringBootApplication
public class ActuatorEndpointApplication {
public static void main(String[] args) {
// SpringApplication.run(ActuatorEndpointApplication.class, args);
SpringApplication app = new SpringApplication(ActuatorEndpointApplication.class);
app.setApplicationStartup(new BufferingApplicationStartup(2048));
app.run(args);
}
}
Now test the url:
http://<HOST>:<port>/actuator/startup in Chrome/Postman.
For more info, refer here

Why does feign client timeout setting (as in documentation) not works?

I try to configure feign client connect timeout, as defined on official documentation, but it does not work.
The app is just simple demo app.
MyFeign.java
package com.example.demo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
#FeignClient(name = "myFeign", url = "localhost:9047/payroll", configuration = FeignConfig.class)
public interface MyFeign {
#GetMapping(value = "/api/master/{employee_id}")
ResponseEntity<String> disableMasterPayroll(#PathVariable(name = "employee_id") String employeeId);
}
1st attempt : using configuration class
FeignConfig.java
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import feign.Request;
#Configuration
public class FeignConfig {
public Request.Options feignRequestOptionsCustom() {
return new Request.Options(5000, 5000);
}
}
2nd attempt, using application.yml as defined on spring doc (copy-paste it)
feign:
hystrix:
enabled: false
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
But none works.
Found several question on stackoverflow (this, this), but none resolve it.
I did not use hystrix. This is my gradle file
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
I'm using boot 2.2 with spring cloud Hoxton.RELEASE.
Any idea how to solve this?

SpringBoot Junit5 fails when renaming package of class holding #SpringBootApplication annotation

I'm struggling with JUnit 5 when moving the #SpringBootApplication to a different package.
I have setup a new SpringBoot-project (2.2.1.RELEASE) with Maven and Eclipse (had to upgrade this from "Eclipse Photon" to support the SpringBoot-Release
My package layout looks like this:
/src/main/java
com.package.sample.appl1
StartSamples.java
com.package.sample.appl1.start
com.package.sample.appl1.dbaccess
com.package.sample.appl1.run
com.package.sample.appl1.utils
com.package.sample.appl2.run
com.package.sample.appl2.run
/src/test/java
com.package.sample.appl1.dbaccess
SimpleTest.java
The class holding the #SpringBootApplication is:
#ComponentScan({
"com.package.sample"
})
#SpringBootApplication
public class StartSamples {
public static void main(String[] args) {
System.out.println("Start");
try {
SpringApplication.run(StartSamples.class, args);
} catch (Exception e) {
LOGGER.error("", e);
System.exit(-1);
}
}
And the test is this:
import static org.junit.Assert.assertEquals;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Test the Query-statements and the DAO methods
*
* #author U005078
*
*/
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ComponentScan({
"com.package.sample"})
#EnableAutoConfiguration
public class SimpleTest {
#SuppressWarnings("unused")
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTest.class);
#Test
#DisplayName("SimpleTest")
public void testTotalRows() {
With this configuration all is fine, "StartSamples" works as expected and aqlso the SimpleTest.
But when moving "StartSamples" to a different package (e.g. "com.package.sample.start" would make more sense to me - "StartSamples" is still ok but "SimpleTest" does not fail nor succeed - test seems not to become executed.
I see a message:
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTest-context.xml] does not exist
class path resource [com/package/sapmle/appl1/dbaccess/SimpleTestContext.groovy] does not exist
.SimpleTest]: SimpleTest does not declare any static, non-private, non-final, nested classes annotated with #Configuration.
I also found:
Neither #ContextConfiguration nor #ContextHierarchy found for test class [com.package.sample.appl1.dbaccess.SimpleTest], using SpringBootContextLoader
So I defined the #ContextConfiguration to the "SimpleTest", then it worked. But I do not understand at all why the move of the #SpringBootApplication did change this behaviour.
With another try of setting up this project I ended up with "No tests found with test runner 'JUnit 5'" and could also not find any reason. I started over again with the current approach and get to here. And do do nat any clue what gives me the error - for either of the problems.
Any explanation witld be appreciated. I tried for lots of hours now to find something in the internet - but I only found recommendations like "try this", "try that" but no help in understanding.
So any help is appreciated.
Define your SpringBoot Main class like below
#SpringBootTest(classes = {StartSamples.class})
public class SimpleTest {
...
}

Spring Boot overriding YML profile from Command Line

I wan't to override an existing YML file profile using command line, so I did this.
Created a folder and added to the classpath
Copied another application.yml in that new folder
Ran this command mvn spring-boot:run -Dspring.profiles.active=unit-test
but it still picking up the "default" active profile from the source code application.yml. I also tried creating a application.properties instead of application.yml but it still didn't get picked up?
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class SuchApplication implements CommandLineRunner {
#Autowired
private DogeService dogeService;
#Override
public void run(String... args) {
System.out.println("AutoConfiguration should have wired up our stuff");
System.out.println("Let's see if we are doge-worthy...");
if (dogeService.requiresDogeness()) {
System.out.println(dogeService.leDoge());
} else {
System.out.println("No Doge for us :(");
}
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SuchApplication.class, args);
}
}
I have the following YML file under my resources folder
spring:
profiles.active: default
---
spring:
profiles: default
doge:
wow: 10
such: so
very: true
---
spring:
profiles: unit-test
doge:
wow: 4
such: so
very: true
I had a similar problem with Spring Boot and resolved it with this annotation in my configuration class...
#PropertySource("classpath:application.yml")
But from the official Spring documentation, this annotation looks unnecessary, which is why I did not add on the first attempt.

How to service external static HTML files in Spring Boot Embedded tomcat?

I'm new to Spring framework and Spring Boot.
I've implemented a very simple RESTful Spring Boot web application.
You can see the nearly full source code in another question: Spring Boot: How to externalize JDBC datasource configuration?
How can the app service external static HTML, css js files?
For example, the directory structure may be as follows:
MyApp\
MyApp.jar (this is the Spring Boot app that services the static files below)
static\
index.htm
images\
logo.jpg
js\
main.js
sub.js
css\
app.css
part\
main.htm
sub.htm
I've read the method to build a .WAR file that contains static HTML files, but since it requires rebuild and redeploy of WAR file even on single HTML file modification, that method is unacceptable.
An exact and concrete answer is preferable since my knowledge of Spring is very limited.
I see from another of your questions that what you actually want is to be able to change the path to static resources in your application from the default values. Leaving aside the question of why you would want to do that, there are several possible answers.
One is that you can provide a normal Spring MVC #Bean of type WebMvcConfigurerAdapter and use the addResourceHandlers() method to add additional paths to static resources (see WebMvcAutoConfiguration for the defaults).
Another approach is to use the ConfigurableEmbeddedServletContainerFactory features to set the servlet context root path.
The full "nuclear option" for that is to provide a #Bean definition of type EmbeddedServletContainerFactory that set up the servlet container in the way you want it. If you use one of the existing concrete implementations they extend the Abstract* class that you already found, so they even have a setter for a property called documentRoot. You can also do a lot of common manipulations using a #Bean of type EmbeddedServletContainerCustomizer.
Is enough if you specify '-cp .' option in command 'java -jar blabla.jar' and in current directory is 'static' directory
Take a look at this Dave Syer's answer implementation.
You can set the document root directory which will be used by the web context to serve static files using ConfigurableEmbeddedServletContainer.setDocumentRoot(File documentRoot).
Working example:
package com.example.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.file.Paths;
#Configuration
public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private static final String STATIC_ASSETS_FOLDER_PARAM = "static-assets-folder";
private final String staticAssetsFolderPath;
public WebConfigurer(Environment env, #Value("${" + STATIC_ASSETS_FOLDER_PARAM + ":}") String staticAssetsFolderPath) {
this.env = env;
this.staticAssetsFolderPath = staticAssetsFolderPath;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length > 0) {
log.info("Web application configuration, profiles: {}", (Object[]) env.getActiveProfiles());
}
log.info(STATIC_ASSETS_FOLDER_PARAM + ": '{}'", staticAssetsFolderPath);
}
private void customizeDocumentRoot(ConfigurableEmbeddedServletContainer container) {
if (!StringUtils.isEmpty(staticAssetsFolderPath)) {
File docRoot;
if (staticAssetsFolderPath.startsWith(File.separator)) {
docRoot = new File(staticAssetsFolderPath);
} else {
final String workPath = Paths.get(".").toUri().normalize().getPath();
docRoot = new File(workPath + staticAssetsFolderPath);
}
if (docRoot.exists() && docRoot.isDirectory()) {
log.info("Custom location is used for static assets, document root folder: {}",
docRoot.getAbsolutePath());
container.setDocumentRoot(docRoot);
} else {
log.warn("Custom document root folder {} doesn't exist, custom location for static assets was not used.",
docRoot.getAbsolutePath());
}
}
}
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
customizeDocumentRoot(container);
}
}
Now you can customize your app with command line and profiles (src\main\resources\application-myprofile.yml):
> java -jar demo-0.0.1-SNAPSHOT.jar --static-assets-folder="myfolder"
> java -jar demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=myprofile

Resources