SpringApplicationBuilder for specified profile over -Dspring.profile.active=local - spring

I have a integrationTest which needs to spin to applications. One at 8181 and 8185 both application needs to behave exactly the same but only difference is to listen on different ports.
I give -Dspring.profile.active=local for 8181 server and for other server I do
applicationContext = new SpringApplicationBuilder(springConfigs)
.profiles("abc")
.run();
But looks like even though I am specifying abc as a profile, other server starts with local profile - hence port 8181.
If I don't specify -Dspring.profile.active=local and use ActiveProfile then all works fine but since I cannot change -Dspring.profile.active=local piece I have to come up with alternate route. Is it possible to force SpringApplicationBuilder to use profile I specify?
Thanks in advance

Based on the order of precedence for spring boot properties give here command line is among the top. But before that is #TestPropertySource and SpringBootTest#properties. The latter takes in an array of strings of form key=value.

Related

Spring-boot #Value properties not overridden by command line arguments

I have a Maven/SpringBootApplication that takes its properties from a Spring config Server. I need to override the values of these properties using command line arguments. unfortunately, the properties keep the values provided by the config server and are not overridden by the command line arguments.
I have confirmed that the parameters are properly passed to the App as I can see being passed to SpringApplication.run.
I can see in the function ConfigurableApplicationContext of Spring Framework the environment carrying the arguments in environment.propertysources.propertySourceList.SimpleCommandLinePropertySource.source.optionArgs
If I try to set a Spring-defined value (e.g. --logging.level.org.springframework.web=TRACE) it works, meaning Spring logs traces
I read all possible threads on the subject but none seem to apply to my problem.
This is my Spring boot app (args are beeing passed to the SpringApplication)
#SpringBootApplication
#ComponentScan("com.mycompany")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Here is the component and the property
#Component
public class TaskProcessor implements com.mycompnay.fwk.task.engine.TaskProcessor {
private RestTemplate restTemplate = new RestTemplate();
#Value("${mycompany.converter.converter-uri.office}")
private String converterUriOffice;
}
The parameter being passed is received by the app (extracted from debugger):
0:"--debug=true"
1:"--logging.level.org.springframework.web=TRACE"
2:"--mycompany.converter.converter-uri.office=foo"
hash:0
value:char[44]#25
I expect the property converterUriOffice to have the value foo
Instead it gets its value from the config server (http://localhost:3000/convert/office)
Links from Devilluminati did the job. Thanks a lot! To make it as clear as possible, here is what I had to do.
1- My application has a matching YML file served by the config server called application.yml
2- Inside the application.yml, I have two profiles and I only wanted the ability to override the arguments while using the local profile.
So here is what I had to add to application.yml:
spring:
profiles: local
cloud:
config:
override-system-properties: false
Once I did that (and restarted the config server to make sure it pulls the latest YML), I am able to override the value above by passing the following to the command line:
--mycompany.converter.converter-uri.office=foo
found following in the documentation https://cloud.spring.io/spring-cloud-static/Edgware.SR2/single/spring-cloud.html#overriding-bootstrap-properties
Overriding the Values of Remote Properties The property sources that
are added to you application by the bootstrap context are often
"remote" (e.g. from a Config Server), and by default they cannot be
overridden locally, except on the command line. If you want to allow
your applications to override the remote properties with their own
System properties or config files, the remote property source has to
grant it permission by setting spring.cloud.config.allowOverride=true
(it doesn’t work to set this locally). Once that flag is set there are
some finer grained settings to control the location of the remote
properties in relation to System properties and the application’s
local configuration: spring.cloud.config.overrideNone=true to override
with any local property source, and
spring.cloud.config.overrideSystemProperties=false if only System
properties and env vars should override the remote settings, but not
the local config files.
same problem here with the solution https://github.com/spring-cloud/spring-cloud-config/issues/907
The documentation is not very clear cause it says that command line
arguments always take precedence over remote configuration.
The property sources that are added to you application by the
bootstrap context are often "remote" (e.g. from a Config Server), and
by default they cannot be overridden locally, except on the command
line.
To achieve that, you have to activate the
spring.cloud.config.override-system-properties=false configuration
(the documentation only talk about System properties but it seems to
be applicable to command line arguments too).

Multiple properties file for a single spring profile

We are using spring boot 2.0.0. We have three environments dev, staging, production. Our current config structure
dev
application-dev.yml
application-dev.properties
Likewise, we have a yml and properties file for each environment. After a year of development now the single yml file for a profile become a large monolithic config.
is it possible to have a multiple config files for a profile like below?
application-dev.yml
application-dev-sqs.yml
application-dev-redis.yml
I think there are 2 ways you can achieve this requirement.
spring.profiles.active accepts a comma-separated list of active profiles, so you can always provide dev,dev-sqs,dev-redis as the value.
Another approach is by making use of #PropertySource and a custom PropertySourceFactory to achieve this requirement. You can find an implementation which takes the value from spring.profiles.active to load one corresponding YAML file in the article below. It should be super easy to adapt the implementation to load multiple files by looking for the profile id in the name of the YAML files.
[How-to] Read profile-based YAML configurations with #PropertySource
I was dealing with a similar problem and I'd recommend using yaml configuration.
Let's describe .properties file:
Initital approach
One can use it like this:
#Component
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("classpath:application-${spring.profiles.active}.properties")
})
public class AppProperties {
}
This is very easy to configure. Limitation is, that you cannot combine profiles. I mean, that when you want to use profile as dev,local where local just alters some config properties for dev profile, Spring will try to load application-dev,local.properties file, which is very likely not what you want.
Btw, this is what Spring will do for you automatically, this is useful for topics as you described.
There is no way to configure it per profile (and not for whole list). Other possibility would be, that one can specify the list in spring.config.name which is not the case at the moment.
Better approach
In short, use:
#Profile("dev")
#Configuration
#PropertySources({
#PropertySource("classpath:topic1-dev.properties"),
#PropertySource("classpath:topic2-dev.properties")
})
public class AppPropertiesDev {
}
Disadvantage is, you have to have several such config classes (dev, staging), but know you have the topics. Also you can use mutliple profiles, which are (as of my testing) loaded in order you specified. That way, your developer can easily use dev configuration and alter just what's needed for his/her testing.
Yaml approach
You can see the approach with yaml in question I asked earlier - Property resolving for multiple Spring profiles (yaml configuration), benefit is smaller amount of files - yaml has all the profiles in one file, which may or may not be what you want.
Yes, it's possible. spring.config.location is used to externalize the config file location in Spring boot applications. This can be used to provide a location of the file in the filesystem or even in the classpath. Based on how you want to provide your application access to the files, you can choose the URI.
Doing it programmatically:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(Application.class)
.properties("spring.config.location:classpath:/application-dev.yml,classpath:/application-dev-sqs.yml,classpath:/application-dev-redis.yml")
.build()
.run(args);
}
}
Doing it via environment variables:
set SPRING_CONFIG_LOCATION=classpath:/application-dev.yml, \
classpath:/application-dev-sqs.yml, \
classpath:/application-dev-redis.yml
So, you can provide your files as comma-separated values.
I've used classpath here, it can also be a location in the file system:
/home/springboot-app/properties/application-dev.yml,/home/springboot-app/properties/application-sqs.yml,/home/springboot-app/properties/application-redis.yml
Have you tried including profiles yet ?
Example with profile default, you want to load additional properties for redis and db. Within application.properties file, add:
spring.profiles.include=redis, db
This will load files application-redis.properties and application-db.properties respectively

Change application.properties from an HTML page

I am working with Spring and have information in my application.properties that I want to update from an HTML page
Myapplication.properties
...
spring.mail.host=smtp.gmail.com
spring.mail.port=587
...
Let say we need to change the port.
Is it possible to do something like that and what is the result if a user is logged in and we made a change?
I also read this post Update property in spring environment in java code is it the right solution.
I guess if I say that we need to rebuild the appplication.properties after changing some information.
Is it possible to do something like that and what is the result if a
user is logged in and we made a change?
if i understood it right, you want to change mail port in runtime? if so :
of course this is possible, but changing the value in property file alone wouldn't result in a actual change in your system, you should know that it is your responsibility to manage the reconstruction of a new mail sender instance in which you should also consider issues like multi-threading , race-condition , etc
I propose you to use application.properties in system startup to initialize your instance, and in case of change use something like this:
taking advatage of the Changing mail configuration in runtime and singleton pattern you should probabaly reach your aim :
#Component
public class MailSender{
#Value("${spring.mail.host}")
public static String host;
#Value("${spring.mail.port}")
private static Integer port
private static JavaMailSender instance;
public static synchronized JavaMailSender getInstance(Integer port) {
if (instance == null || port!= null) {
MailSender.port = port!=null ? port: MailSender.port;
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(MailSender.host);
mailSender.setPort(MailSender.port);
return instance;
}
}
The above code is an alteration of singleton pattern in which we check whether the mail instance is null or port has new value recreate the instance otherwise if instance has already a value use that, in this way you can change port run time.
please notice that by the code above I am trying to give you some insight into the problem and you may change it based on your design.
I hope I got your purpose correctly.
First of all, after changing configuration you have to reload beans dependent on changed variables.
I would recommend you to have a look at Spring Cloud Config project.
It has the following features:
Stores configs (and changes) in Git
Can change configuration properties at runtime, and force subscribed applications to reload their context (or even dependent beans only) automatically
Despite it is not direct answer to your question (it doesn't have an UI for configuration), but it is a good reason to search UI for Spring Cloud Config instead.

How to test two Infinispan with JUnit?

I want to create test to make sure two instances of Infinispan cache are communication well.
In first step I create two applicationContexts using two different application-test.properties
In logs I can see two instances of cache are created.
In debug I can see also two different instances of CacheManager | DefaultCacheManager.
Everything looks fine - but when I add some valued to one instance second one instance of Cache (Infinispan) is not notified about that.
Any advice?
Currently you can use NoSQLUnit https://github.com/lordofthejars/nosql-unit#infinispan-engine which gives support for testing and managing lifecycle of Infinispan.
In next weeks we are going to integrate this to Arquillian APE as well.
If you have any question don't hesitate to ping me, my twitter is #alexsotob
if you have problem with start two Infinispan's caches on local machine try to use real host name or IP instead 'localhost' or '127.0.0.1'
if you have problem with multiple JUnit tests and Infinispan's caches try to stop transport after each test - like:
#After
public void tearDown() {
applicationContext.getBean(CacheManager.class).getTransport().stop();
}
Create a file infinispan.xml
< infinispan
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
xmlns="urn:infinispan:config:5.1">
<namedCache name="xml-configured-cache">
<eviction strategy="LIRS" maxEntries="10" />
</namedCache>
< / infinispan >
Init cache with file configuration:
Cahe c = new DefaultCacheManager("infinispan.xml").getCache("xml-configured-cache");
That's all!

Spring Boot: specify port at the mapping level

Spring Boot: I want to have achieved the following: some URL paths are mapped to a port, some to another.
In other words I'd like something like:
public class Controller1 {
#RequestMapping(value="/path1", port="8080") public...
#RequestMapping(value="/path2", port="8081") public...
}
So that my app responds to both localhost:8080/path1 and localhost:8081/path2
It's acceptable to have 2 separate controllers within the app.
I have managed to partially succeed by implementing an EmbeddedServletContainerCustomizer for tomcat, but it would be nice to be able to achieve this inside the controller if possible.
Is it possible?
What you are trying to do would imply that the application is listening on multiple ports. This would in turn mean that you start multiple tomcat, since spring-boot packages one container started on a single port.
What you can do
You can launch the same application twice, using different spring profiles. Each profile would configure a different port.
2 properties:
application-one.properties: server.port=8080
application-two.properties: server.port=8081
2 controllers
#Profile("one")
public class Controller1 {
#RequestMapping(value="/path1") public...
}
#Profile("two")
public class Controller2 {
#RequestMapping(value="/path2") public...
}
Each controller is activated when the specified spring profile is provided.
Launch twice
$ java -jar -Dspring.profiles.active=one YourApp.jar
$ java -jar -Dspring.profiles.active=two YourApp.jar
While you cannot prevent making call on the undesired port, you can specify HttpServletRequest among other parameters of the method of the controller, and then use HttpServletRequest.getLocalPort() to obtain the port the call is made on.
Then you can manually return the HTTP error code if the request is made on the wrong port, or forward to another controller if the design is such that same path on different ports must be differently processed.

Resources