Spring Auto Create Properties File - java

I have a simple class to represent property file using #PropertySource like so
#Component
#PropertySource(value = "file:${user.home}/.cooperp/app-conf.properties")
public class ApplicationProperties {
#Value("${mysql-location}")
private String mysqlLocation;
public String getMysqlLocation() {
return mysqlLocation;
}
public void setMysqlLocation(String mysqlLocation) {
this.mysqlLocation = mysqlLocation;
}
}
I know if file is not found one can add ignoreResourceNotFound = true which will make spring to ignore its absence and startup the application.
I would like spring to create the file it not found, not ignore or throw exception.

Usually we keep the properties file withing the project directory. But if your project requires the properties outside then you can check if the file exists in SpringBootApplication class. If it doesen't, then you can create the properties file there. Code example :
#SpringBootApplication
public class SpringDocBotApplication {
public static void main(String[] args) {
File file = new File("EXPECTED PATH");
try{
if(!file.exists()){
// create propeties file add properties
}
} catch (IOException e) {
//HANDLE EXCEPTION
}
SpringApplication.run(SpringDocBotApplication.class, args);
}
}

Related

JAVA Spring Boot : How to access application.properties values in normal class

I know how I can access the application.properties values in #Service classes in Java Spring boot like below
#Service
public class AmazonClient {
#Value("${cloud.aws.endpointUrl}")
private String endpointUrl;
}
But I am looking for an option to access this value directly in any class (a class without #Service annotation)
e.g.
public class AppUtils {
#Value("${cloud.aws.endpointUrl}")
private String endpointUrl;
}
But this returns null. Any help would be appreciated.
I have already read here but didn't help.
There's no "magic" way to inject values from a property file into a class that isn't a bean. You can define a static java.util.Properties field in the class, load values from the file manually when the class is loading and then work with this field:
public final class AppUtils {
private static final Properties properties;
static {
properties = new Properties();
try {
ClassLoader classLoader = AppUtils.class.getClassLoader();
InputStream applicationPropertiesStream = classLoader.getResourceAsStream("application.properties");
applicationProperties.load(applicationPropertiesStream);
} catch (Exception e) {
// process the exception
}
}
}
You can easily achievw this by annotating ur app utils class with #component annotation . spring will take care of loading properties.
But if you don't want to do that approach , then look at the link below .
https://www.baeldung.com/inject-properties-value-non-spring-class

How to mock a .properties file injected with #PropertySource in #Configuration?

My application expects to find a configuration file called MyPojo.json, loaded into MyPojo class by MyService class:
#Data // (Lombok's) getters and setters
public class MyPojo {
int foo = 42;
int bar = 1337;
}
It's not a problem if it doesn't exist: in that case, the application will create it with default values.
The path where to read/write MyPojo.json is stored in /src/main/resources/settings.properties:
the.path=cfg/MyPojo.json
which is passed to MyService through Spring's #PropertySource as follows:
#Configuration
#PropertySource("classpath:settings.properties")
public class MyService {
#Inject
Environment settings; // "src/main/resources/settings.properties"
#Bean
public MyPojo load() throws Exception {
MyPojo pojo = null;
// "cfg/MyPojo.json"
Path path = Paths.get(settings.getProperty("the.path"));
if (Files.exists(confFile)){
pojo = new ObjectMapper().readValue(path.toFile(), MyPojo.class);
} else { // JSON file is missing, I create it.
pojo = new MyPojo();
Files.createDirectory(path.getParent()); // create "cfg/"
new ObjectMapper().writeValue(path.toFile(), pojo); // create "cfg/MyPojo.json"
}
return pojo;
}
}
Since MyPojo's path is relative, when I run this from a Unit Test
#Test
public void testCanRunMockProcesses() {
try (AnnotationConfigApplicationContext ctx =
new AnnotationConfigApplicationContext(MyService.class)){
MyPojo pojo = ctx.getBean(MyPojo.class);
String foo = pojo.getFoo();
...
// do assertion
}
}
the cfg/MyPojo.json is created under the root of my project, which is definitely not what I want.
I would like MyPojo.json to be created under my target folder, eg. /build in Gradle projects, or /target in Maven projects.
To do that, I've created a secondary settings.properties under src/test/resources, containing
the.path=build/cfg/MyPojo.json
and tried to feed it to MyService in several ways, without success.
Even if called by the test case, MyService is always reading src/main/resources/settings.properties instead of src/test/resources/settings.properties.
With two log4j2.xml resources instead (src/main/resources/log4j2.xml and src/test/resources/log4j2-test.xml), it worked :/
Can I do the same with a property file injected by Spring with #PropertySource ?
You can use #TestPropertySource annotation for this.
Example:
For single property:
#TestPropertySource(properties = "property.name=value")
For property file
#TestPropertySource(
locations = "classpath:yourproperty.properties")
So, you provide path for MyPojo.json like
#TestPropertySource(properties = "path=build/cfg/MyPojo.json")

Spring boot cron expression scheduling, calling onApplicationEvent of ApplicationListener

I have written a StartupListener by implementing ApplicationListener and overriding the method: onApplicationEvent(ContextRefreshedEvent event).
#Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger logger = LoggerFactory.getLogger(StartupListener.class);
#Value("${create.file.some.merchant}")
private boolean createMerchantAFile;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
logger.info("Application context started");
if (createMerchantAFile) {
EmbeddedResponse emb = null;
file = ReportConstants.MERCHANT_A+ ReportConstants.FILE_FORMAT;
try {
emb = genericServices.readJsonFile("merA.json");
generateReport.generateExcelFile(emb, file, "MerchantA");
} catch (IOException ioe) {
logger.error("IO Exception while reading JSON file. Message: ", ioe);
} catch (Exception e) {
logger.error("Exception while reading JSON file. Message", e);
}
createMerchantAFile= false;
}
}
}
Inside this method, I am trying to create some files based on whether a boolean value corresponding to file is true or not.
This boolean value is being read from "application.properties" file by using #Value annotation.
This StartupListener works fine.
Now I wish to generate these files by scheduling them, so I added #EnableScheduling to my main class file, and created a new java file with a method:
#Component
public class FileGenerationScheduler {
#Autowired
StartupListener startupListener;
#Scheduled(cron = "${file.gen.cron}")
public void generateFileWithCron() {
startupListener.onApplicationEvent(null); //passing null here
}
}
This method gets called on the specified cron expression but all the #Value boolean values are not read from "application.properties". So by default these values will be false (instance variables)
#Value("${create.file.some.merchant}")
private boolean createMerchantAFile;
This is present in StartupListener and is now false. So nothing is created.
How can I make sure that these values are read from application.prop even when called via scheduler ?
Create a service and add all the logic in it including the fetching of the value from the property find . Inject that bean inside the listener and the scheduler class.

Java Spring Boot Configurations & Components

I'm trying to use a component to load up my configuration yml file.
However, it throws a Null Pointer exception and System.out shows 'null' for application.
However, when the same pattern is used to code up a #RestController, everything works fine. Why is #component not seeing my configurations??
#Component
#ConfigurationProperties(prefix = "myconf")
public class AppConfigJSON {
private String application;
private final String applicationConfigJSON;
Logger logger = LoggerFactory.getLogger(this.getClass());
private final ReadContext acRcJson;
public AppConfigJSON(){
String json = "";
try {
System.out.println("Application: " + this.application);
json = IOUtils.toString(this.getClass().getResourceAsStream("/myconfs/"+this.application+".json"));
} catch (IOException e) {
logger.error("Error reading JSON or app YML {}", e.getStackTrace());
}
this.applicationConfigJSON = json;
this.acRcJson = JsonPath.parse(this.getApplicationConfigJSON());
}
// These functions below set by configuration
public String getApplication()
{
return application;
}
public void setApplication(String application)
{
this.application = application;
}
}
In your constructor the application variable hasn't been initialized yet, in other words, Spring needs an instance first so it can apply it's magic. You need to move your constructor logic to a method annotated with #PostContruct so the application variable is set with the property value at that point.

Reading entire property file loaded by PropertySource in spring bean

I've a spring bean which loads the property file depending upon their availability as shown below:-
#PropertySources({ #PropertySource(value = "classpath:user.properties"),
#PropertySource(value = "file:./config/user.properties", ignoreResourceNotFound = true) })
The property file is getting loaded, but when I try to read entire property file in one go via :-
Properties properties = PropertiesLoaderUtils.loadAllProperties("user.properties");
then I only get the properties from classpath. Do spring provide any mechanism to read all properties in one go?
That code of yours doesn't do what the annotations do. You have a couple of annotations that declare what to do. That logic isn't present at all in the code snippet.
There's no magic, if you want the same result, you need to translate the declarative aspects of those annotations in code (i.e. reading the classpath file then the file one and check if it exists and then merge those properties).
If you're ok to get extra keys, you could also simply inject the Environment as #PropertySource is going to update that.
Answering my own question, may be this may help someone.
Since I need to override the properties file contained in jar with external properties file (if present in specified folder) also I need to read entire property file in one go.
I've leveraged the spring behavior of loading last property read.
#PropertySources({ #PropertySource(value = "classpath:application.properties"),
#PropertySource(value = "file:./config/application.properties", ignoreResourceNotFound = true) })
Now if application.properties is present in ./config/ location then it'll override application.properties from classpath.
In main application.properties I've defined from where the external properties should get loaded i.e.
config.location=./config/
./config/ attribute can be overridden in case of production and test environment.
After this I've defined a bean to load all properties files (import statement skipped):-
#Component
public class PropertiesConfig {
private final Logger logger = LoggerFactory.getLogger(PropertiesConfig.class);
private final String[] PROPERTIES_FILENAMES = { "prop1.properties", "prop2.properties",
"prop3.properties" };
private String configLocation;
private Map<String, Properties> configProperties;
#Autowired
public PropertiesConfig(#Value("${config.location}") String configLocation) {
this.configLocation = configLocation;
configProperties = Arrays.stream(PROPERTIES_FILENAMES)
.collect(Collectors.toMap(filename -> filename, this::loadProperties));
}
public Properties getProperties(String fileName) {
if (StringUtils.isEmpty(fileName) || !configProperties.containsKey(fileName)) {
logger.info(String.format("Invalid property name : %s", fileName));
throw new IllegalArgumentException(
String.format("Invalid property name : %s", fileName));
}
return configProperties.get(fileName);
}
private Properties loadProperties(final String filename) {
final Resource[] possiblePropertiesResources = { new ClassPathResource(filename),
new PathResource(getCustomPath(filename)) };
final Resource resource = Arrays.stream(possiblePropertiesResources)
.filter(Resource::exists).reduce((previous, current) -> current).get();
final Properties properties = new Properties();
try {
properties.load(resource.getInputStream());
} catch (final IOException exception) {
throw new RuntimeException(exception);
}
logger.info("Using {} as user resource", resource);
return properties;
}
private String getCustomPath(final String filename) {
return configLocation.endsWith(".properties") ? configLocation : configLocation + filename;
}
}
Now you have a bean containing all the properties file in map which can be injected in any bean and can be overridden for any environment.

Categories