#ConfigurationProperties not pulling the external properties file - spring-boot

I've created a personal repository on Git where I have kept my application.properties file.
I've created a cloud config server ('my-config-server') and used the git repository url.
I have bound my spring-boot application that is supposed to access the external properties file with Git repository.
#javax.jws.WebService(
serviceName = "myService",
portName = "my_service",
targetNamespace = "urn://vdc.com/xmlmessaging/SD",
wsdlLocation = "classpath:myService.wsdl",
endpointInterface = "com.my.service.SDType")
#PropertySource("application.properties")
#ConfigurationProperties
public class SDTypeImpl implements SDType {
/*It has various services implementation that use following method**/
private SDObj getObj (BigDecimal value) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(
SDTypeImpl.class);
SDObj obj = context.getBean(SDPropertiesUtil.class).getObj(value);
context.close();
return obj;
}
}
Another Class:
public class SDPropertiesUtil {
#Autowired
public Environment env;
public SDObj getObj(BigDecimal value) {
String valueStr = env.getProperty(value.toString());
/*do logic*/
}
My application starts but fails to load properties file from my git repository.
I believe I should have an application.properties at src/main/resources in my application but since I'm using
#PropertySource("application.properties")
#ConfigurationProperties
I'm telling my application to use the application.properties from an external location and do not use internal properties file. But this is not happening. My application is still using the internal properties file.

The source you included doesn't show your app configuration settings to connect to the Config server. Do you mind sharing it?
This is how the config server could be queried from a client app:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
Let's say a Config server points to a Git repo which includes this file: demo-config-client-development.properties
You should be able to query the Config Server as:
curl http://localhost:8101/demo-config-client-development.properties
Assuming Config Server is running in locally and listening on 8181.
Let's also say you have a client app named: demo-config-client that connects to the Config server and runs using the development Spring profile, this app would now be able to read remote properties hosted in a Git repo through a Config server.
A detailed tutorial could be found at my blog at: http://tech.asimio.net/2016/12/09/Centralized-and-Versioned-Configuration-using-Spring-Cloud-Config-Server-and-Git.html

Related

Spring Boot - Multi Document File based on profile for Tomcat 9.0.54

I have an application.properties file in Spring Boot v2.6.1 where I declared a multi document file notation like below :
spring.profiles.active=#spring.profiles.active#
#---
spring.config.activate.on-profile=prod
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
#---
spring.config.activate.on-profile=dev
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
This works perfectly fine(i.e., picked accordingly) when I run the application in integrated server or IDE by passing spring.profiles.active as prod/dev in VM Arguments.
The same thing doesn't work when I deploy as a war in tomcat by passing in setenv.sh as
export CATALINA_OPTS="$CATALINA_OPTS -Dspring.profiles.active=prod"
it always picks the "org.hibernate.dialect.MySQL8Dialect" instead of "org.hibernate.dialect.Oracle12cDialect"
Any help?
After a day of brain storming, finally I found the solution to pick the respective dialect when using multiple datasources based on the profile.
In my case, Primary datasource is Oracle with UCP and secondary is mySQL for Prod & dev profiles respectively.
As per the question, multi document file notation in application.properties works fine in IDE or integrated tomcat but not in External tomcat when deployed as a WAR.
Below solution works for both (Integrated & External Tomcat)
In MySQL Configuration class, I have set the custom JPA Properties as a tweak.
#Configuration
#Profile(someConstants.ENV_DEV)
public class MySqlConfiguration {
private static final Logger logger = LogManager.getLogger(MySqlConfiguration.class);
#Bean(name = "mySQL")
#Profile(someConstants.ENV_DEV)
#ConfigurationProperties(prefix = "spring.mysql.datasource")
public DataSource dataSource() {
final String METHOD_NAME = ":: DataSource ::";
logger.info(METHOD_NAME + "Initialising the MySQL Connection");
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.mysql.jpa")
public JpaProperties jpaProperties() {
JpaProperties properties = new JpaProperties();
return properties;
}
}
In Application.properties
spring.mysql.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect // this for mySQL
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect // this for Oracle
Moreover, I have done the same setting in setenv.sh file for external tomcat
// for prod
export CATALINA_OPTS="$CATALINA_OPTS -Dspring.profiles.active=prod"
// for dev
#export CATALINA_OPTS="$CATALINA_OPTS -Dspring.profiles.active=dev"
I analysed the logs and now each datasources picks up the respective properties & dialect based on profile, perfectly fine and awesome.
Happy Coding..

Integration Test with Spring Cloud Config Server

It's my first time implementing a Spring application using Spring Cloud Config framework. The application is working fine, getting configuration properties from repository through the server application. So, at the moment I must write the integration tests to test connection between client application and server and repository. To do it I mean get a value from repository over properties and check the value, also make a request to the server, or if exists, other method inside spring cloud config library to make it.
The problem now, when executing the integrationTest the application can't read the properties from remote. I've created a bootstrap.yml on the integrationTest context but not works. So, I got the errors like: Could not resolve placeholder.
MyIntegrationTest
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest()
#ActiveProfiles("integrationTest")
public class MyIntegrationTest {
#Value("${throttling.request.rate.minute}")
private int MAX_REQUEST_PER_MINUTE;
#Test
public void validateGetRequestRateLimitFromRepository() {
Assert.assertNotEquals(MAX_REQUEST_PER_MINUTE,1L);
}
}
bootstrap.yml
spring:
application:
name: sample-name
---
spring:
profiles: local #also tried integrationTest
cloud:
config:
enabled: true
uri: https://endpoint-uri:443
skipSslValidation: true
name: ${spring.application.name},${spring.application.name}-${spring.profiles}
Also tried:
Set application-integrationTest.yml
Set application.yml
Set bootstrap-integrationTest.yml
Create a *-integrationTest.yml at the repo.
Set #ActiveProfiles("local") on integrationTest class.
Nothing works.

Spring Integration FTP in Docker container: not triggering flow

I am having quite a time figuring out where my issue is stemming from. I can run locally, I have built my .jar and ran that locally too.
I have my integration flow set as follows
#Bean
IntegrationFlow integrationFlow(final DataSource datasource) {
return IntegrationFlows.from(
Ftp.inboundStreamingAdapter(template())
.remoteDirectory("/folder/")
.patternFilter("file_name.txt")
.filter(
new FtpPersistentAcceptOnceFileListFilter(metadataStore(datasource), "")),
spec -> spec.poller(Pollers.fixedDelay(5, TimeUnit.SECONDS)))
.transform(streamToBytes())
.handle(handler())
.get()
}
#Bean
FtpRemoteFileTemplate template() {
return new FtpRemoteFileTemplate(ftpSessionFactory());
}
#Bean
public StreamTransformer streamToBytes() {
return new StreamTransformer(); // transforms to byte[]
}
#Bean
public ConcurrentMetadataStore metadataStore(final DataSource dataSource) {
return new JdbcMetadataStore(dataSource);
}
#Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost(host);
sf.setPort(port);
sf.setUsername(userName);
sf.setPassword(password);
return sf;
}
I have my datasource and my ftp information set in my application.yml
When I run this locally, I have no problems. When I run gradle build and run my .jar with several different openjdk versions (8u181, 8u191, 11.04), I have no issues.
When I run inside of a docker container using my .jar file, the problem arises.
My docker file
FROM openjdk:8u212-jdk-alpine
WORKDIR /app
COPY build/libs/app-1.0.jar .
RUN apk add --update ttf-dejavu && rm -rf /var/cache/apk/*
ENTRYPOINT ["java", "-jar", "app-1.0.jar"]
I turned DEBUG on and watched output.
Running locally and running the built .jar, I can see that the poller is working and it triggers the SQL queries to the metadataStore table that has been created in my remote db (postgresql).
Running in the docker container, I do not see the sql queries being run. which tells me that somewhere therein lies the issue.
With debug on my startup logs in the console are the same INFOs and WARNs regardless of running locally, running the built .jar, or running in the docker container.
There is this info message that might be of some assistance
Bean with key 'metadataStore' has been registered as an MBean but has no exposed attributes or operations
I checked to see if there might be a hidden SessionFactory connection issue by trying to connect to an invalid host, but I indeed get exceptions in my docker container for the invalid host. So I can confidently say that the FTP connection is valid and running with the correct host and port.
I am thinking it has to do with either the poller or my datasource.
Inside of this application, I am also running Spring Data Rest using JDBC and JPA, would there be any issue with the usage of the datasource bean across the different libraries?
Any help or guidance would be greatly appreciated.
so the default client mode for the DefaultFtpSessionFactory is "ACTIVE", but in my case, inside of a docker container, the client mode must be set to "PASSIVE"
to do this, i needed to add one line of code to the DefaultFtpSessionFactory
you must set client mode to 2 ... sf.setClientMode(2);
below is the final DefaultFtpSessionFactory bean.
#Bean
public SessionFactory<FTPFile> ftpSessionFactory() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost(host);
sf.setPort(port);
sf.setUsername(userName);
sf.setPassword(password);
sf.setClientMode(2);
return sf;
}

Spring ignores `spring.cloud.config.discovery` set as default properties

I'm trying to create Spring Application without referring to any external files. This is supposed to be a module that you'd then include as a dependency, configure and use to plug in the service into an existing ecosystem. This is how I'm doing that:
Map<String, Object> properties = new HashMap<>();
properties.put("server.address", "0.0.0.0")
properties.put("server.port", 8080)
properties.put("spring.profiles.active", "cloud")
properties.put("spring.application.name", "someApp")
properties.put("spring.cloud.config.failFast", true)
properties.put("spring.cloud.config.discovery.enabled", true)
properties.put("spring.cloud.config.discovery.serviceId", "config")
properties.put("eureka.instance.preferIpAddress", true)
properties.put("eureka.instance.statusPageUrlPath", "/health")
new SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.properties(properties)
.sources(SpringConfiguration.class)
.web(false)
.registerShutdownHook(true)
.build()
I then go on to provide Eureka default zone in the run command, via environmental variables:
--env eureka_client_serviceUrl_defaultZone='http://some-host:8765/eureka/' --env SPRING_CLOUD_CONFIG_LABEL='dev' --env SPRING_CLOUD_INETUTILS_PREFERRED_NETWORKS='10.0'
Application registers successfully in Eureka, but unfortunately it tries to fetch the config prior to that and it's looking for it under the default URL (http://localhost:8888) instead of fetching config server IP from the registry. And yes, it does work if I put all of those properties in the bootstrap.yml file. Can I somehow make it work without using file-resources?
You are passing the properties using SpringApplicationBuilder which is responsible for SpringApplication and ApplicationContext instances.
From the documentation , the properties provided here will be part of ApplicationContext NOT the BootstrapContext. ApplicationContext is the child of BootstrapContext.
You can read more about the Bootstrap Context here -
http://cloud.spring.io/spring-cloud-commons/1.3.x/single/spring-cloud-commons.html#_the_bootstrap_application_context
Bootstrap.yml/properties is used to configure your Bootstrap Context.
You can look at these properties to change the name or location of the file -
spring.cloud.bootstrap.name - bootstrap(default)
spring.cloud.bootstrap.location
You will have to use a file resource(yml or properties).

How to externalize configuration in Spring Boot using profiles?

I have an application where I would like to change a datasource password that is stored in a application.yml file. The password in the YML file is stored such as this:
----
spring:
profiles: production
datasource:
password: prodpassword
Note: I also have profiles for development and stage.
The password prop is set on a class using ConfigurationProperties such as follows:
#Component
#ConfigurationProperties(prefix="datasource")
public class DataSourceConnector {
private password;
public void setPassword(String password) {
this.password = password;
}
Now, I try to override the prodpassword with prodpa$$word via a command line arg but it doesn't work:
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --datasource.password='prodpa$$word'
I also tried creating an identical (except the new password) application.yml file outside of the jar. That doesn't work either.
java -Dspring.profiles.active=production -jar /usr/share/myapp/myapp-1.0.jar --spring.config.location=/usr/share/myapp/
Note: I left out the file name in the location param due to this note from http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-profile-specific-properties:
If you have specified any files in spring.config.location, profile-specific variants of those files will not be considered. Use directories inspring.config.location if you also want to also use profile-specific properties.
How can I override datasource.password within the application.yml of the jar?
Edit:
The application is being started / stopped using supervisorctl.
After changing the config file that contains the java command, supervisorctl must reread the change:
supervisorctl reread
Next, activate the changes with:
supervisorctl update

Resources