spring boot app cannot load bundle properties files - spring

I am building an app that mostly provide REST services, nothing fancy. since my data consumed by the app can have multiple languages I thought about using the bundle files.
I created 3 files, one with the default file name and another two with specific languages. The files created using intellij IDE I am using.
I followed this guide https://www.baeldung.com/java-resourcebundle however on each run I am getting:
MissingResourceException: Can't find bundle for base name tp_app_strings, locale en_US
I tried numerous articles but none of them seems to resolve the issue.
One fun fact is that if I am using the #Value("classpath:tp_app_strings.properties") on a 'Resource' field I am able to get a reference to that file, so it spring is able to find it.
Additional thing that I tried was to create a WEB-INF directory and place the files there (read it in some article) but still no positive affect
The project structure is quite straight forward:
Spring boot version 2.2 running tomcat.
Any suggeestions would be highly appriciated

You can load the .properties file to the application context using #PropertySource annotation instead using #Value to load the .properties file to a org.springframework.core.io.Resource instance.
The usage;
#Configuration
#PropertySource("classpath:tp_app_strings.properties")
public class DefaultProperties {
#Value("${property1.name}") // Access properties in the above file here using SpringEL.
private String prop1;
#Value("${property2.name}")
private String prop2;
}
You wouldn't need java.util.ResourceBundle access properties this way. Use different or same class to load other .properties files as well.
Update 1:
In order to have the functionality of java.util.ResourceBundle, you can't just use org.springframework.core.io.Resource class. This class or non of it sub-classes don't provide functions to access properties by its name java.util.ResourceBundle whatsoever.
However, if you want a functionality like java.util.ResourceBundle, you could implement something custom like this using org.springframework.core.io.Resource;
#Configuration
public class PropertyConfig {
#Value("classpath:tp_app_strings.properties")
private Resource defaultProperties;
#Bean("default-lang")
public java.util.Properties getDefaultProperties() throws IOException {
Properties props = new Properties();
props.load(defaultProperties.getInputStream());
return props;
}
}
Make sure to follow correct naming convention when define the property file as java.util.Properties#load(InputStream) expect that.
Now you can #Autowire and use this java.util.Properties bean wherever you want just like with java.util.ResourceBundle using java.util.Properties#getProperty(String) or its overloaded counterpart.

I think it's problem of you properties file naming convention. use underline "_" for specifying locale of file like
filename_[languageCode]_[regionCode]
[languageCode] and [regionCode] are two letters standard code that [regionCode] section is optional
about code abbrivation standard take a look on this question
in your case change file name to tp_app_strings_en_US.properties

Related

Spring Boot application to have separated multiple property files

Coming from Play Framework, a handy feature that has helped to organize the application configurations was to use includes (Link) to spilt the various configurations into multiple .conf files as below.
application.conf Content
include "play-http.conf"
include "play-modules.conf"
include "play-i18n.conf"
include "authentication.conf"
include "hbase.conf"
include "custom-caches.conf"
include "custom-filters.conf"
#Any other root level application configurations
Is there an equivalent to this in Spring Boot .properties files?
From Spring 2.4, we can create multiple properties file for each profiles as below.
application-main1.properties
application-sub1.properties
application-sub2.properties
And then in default application.properties file we can group all sub profiles and activate the main profile
spring.profiles.group.main1=sub1,sub2
spring.profiles.active=main1
I am not sure if we can group sub profiles under default profile. You can try out
spring.profiles.group.default=sub1,sub2
This way you don't need to have another file for main profile.
I use yaml configuration files myself but I think that the configuration is mostly similar. You should take a look at the PropertySourcesPlaceholderConfigurer.
I have defined a PropertySourcesPlaceholderConfigurer bean to use a configuration override file located outside of the jar. Anything that is in the override file will be used instead of the default configuration. Anything that is not in the override file is still retrieved from the default configuration file. I think you can create a similar bean to achieve what you are looking for.
Here's my code:
#Bean
static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
var properties = new PropertySourcesPlaceholderConfigurer();
properties.setLocation(new FileSystemResource("./application.yaml"));
properties.setIgnoreResourceNotFound(true);
return properties;
}
For my use case, I only needed to define one properties location, but it is also possible to specify multiple locations:
...
properties.setLocations(Resource... locations);
...
My requirement was simply achieved using the spring.config.import (Link).
I created multiple property files such as hbase.properties, custom-caches.properties etc. And then in my application.properties imported those additional property files as below.
spring.config.import=hbase.properties,custom-caches.properties
#Any other properties in the application.properties file
Thanks

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

What is advantage of using #value annotation in Spring Boot

I am new to Spring Boot and I am doing code cleanup for my old Spring Boot application.
Below code is using #Value annotation to inject filed value from properties file.
#Value("${abc.local.configs.filepath}")
private String LOCAL_ABC_CONFIGS_XML_FILEPATH;
My doubt is instead of getting value from properties file, can we not directly hardcode the value in same java class variable?
Example: private String LOCAL_ABC_CONFIGS_XML_FILEPATH="/abc/config/abc.txt"
It would be easier for me to modify the values in future as it will be in same class.
What is advantage of reading from properties file, does it make the code decoupled ?
This technique is called as externalising configurations. You are absolutely right that you can have your constants defined in the very same class files. But, sometimes, your configurations are volatile or may change with respect to the environment being deployed to.
For Example:
Scene 1:
I have a variables for DB connection details which will change with the environment. Remember, you will create a build out of your application and deploy it first to Dev, then take same build to stage and finally to the production.
Having your configurations defined externally, helps you to pre-define them at environment level and have same build being deployed everywhere.
Scene 2:
You have already generated a build and deployed and found something was incorrect with the constants. Having those configurations externalised gives you a liberty to just override it on environment level and change without rebuilding your application.
To understand more about externalising techniques read: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Here #value is used for reading the values from properties file (it could be a any environment like dev, qa, prod) but we are writing #value on multiple fields it is not recomonded so thats instead of #value we can use #configurableProperties(prefix="somevalue>) and read the property values suppose `
#configurableProperties(prefix="somevalue")
class Foo{
string name;
string address;
}
application.properties:
somevalue.name="your name"
somevalue.address="your address"
`

Look up a dynamic property at run-time in Spring from PropertySourcesPlaceholderConfigurer?

Not sure of the best approach to this. We've created a jar that could be used by different projects. The other projects relying on this jar need to provide certain properties defined in one of their spring properties files. (Our jar shouldn't care what they name those property files.)
Using #Value("${some.prop}") works great for most properties, however we now have the requirement that the name of the property to look up is dynamic. For example:
int val = getSomeVal();
String propNeeded = foo.getProperty("foo."+val+".dynamic.prop");
Not sure what "foo" should be to get my access. I looked into injecting Environment, however from all my googling it looks like that will not load from an xml property-placeholder definition (even if defined as a bean def for PropertySourcesPlaceholderConfigurer.) You seem to have to use #PropertySource, yet my main config is an XML file so not sure how to get Environment to work. (I can't really go 'old skool' and look up the property file as a class path Resource either since I'm not aware of the name of the file the users defined.)
I don't mind making this particular Service class ApplicationContextAware, but if I did that how could I get access to the underlying PropertySourcesPlaceholderConfigurer ? which I would 'seem?' to need in order to get access to a property dynamically?
The other option is that I force users of the jar to declare a bean by a name that I can look up
<util:properties id="appProps" location="classpath:application.properties" />
And I then inject appProps as Properties and look up from there. I don't like this approach though since it forces the users of the library to name an file by a common id. I would think the best solution is to just get a handle in some way to the underlying PropertySourcesPlaceholderConfigurer in my service class... I'm just not sure how to do it?
Why doesn't Spring simply allow PropertySource to be defined some how via your XML config and then I could just inject Environment?
Thanks for any suggestions how to accomplish what I want.
You could have a ReloadableResourceBundleMessageSource declared to read from the same source as the PropertySourcesPlaceholderConfigurer. This way you could just #Autowire MessageSource (or make your bean implement MessageSourceAware) and use that to retrieve your properties.
Main reason for using ReloadableResourceBundleMessageSource is to retrieve I18N messages, so that would kind of hacky...

OSGi bundle config without managed service or factory

Neil Bartlett's article http://njbartlett.name/2010/07/19/factory-components-in-ds.html shows the way to set config for bundles without using managed service or managed factory.
Search for examples of actually setting the config for this method either point to felix file install or to examples using managed service.
In answer to the question OSGi Declarative Services vs. ManagedService for configuring service? Neil Bartlett states "Note that DS never actually creates a ManagedService or ManagedServiceFactory for your component. It works by listening to Config Admin with a ConfigurationListener. However the internal details are unimportant... simply create configs with PID/factoryPID matching the component.name and it "just works"
I think the technique involves placing a pid entry in the config dictionary but I have no idea how this would be used with config admin.
A guide or simple example of how to set the configuration using this method would be very helpful.
I know it is some time since the question was asked, but I ran into the same problem when trying to create a ManagedServiceFactory-like Component with Declarative Services. So I want to share my solution. Maybe others find it useful. My problem was like this:
I have defined a component (annotated with #Component). On each configuration I add using felix file-install, I want an instance of that component created with the given configuration and activated immediately.
First I tried messing with the properties factory and configurationPid of #Component, but all that is not needed and even returns wrong results (felix annotation processor in the maven plugin seems to have a bug when handling configurationPid).
The solution I came up with:
package com.example.my;
#Component(
name = MyExampleComponent.FACTORY_PID,
configurationPolicy = ConfigurationPolicy.REQUIRE,
property = {"abc=", "exampleProp="}
)
public class MyExampleComponent {
public static final String FACTORY_PID = "com.example.my.component";
#Activate
protected void activate(BundleContext context, Map<String,Object> map) {
// ...
}
}
Then I created a config file for felix file-install named com.example.my.component-test1.cfg:
abc = Hello World
exampleProp = 123
When deployed this automatically creates a folder structure in the configuration folder like com/example/my/component containing the files:
factory.config
contents:
factory.pid="com.example.my.component"
factory.pidList=[ \
"com.example.my.component.525ca4fb-2d43-46f3-b912-8765f639c46f", \
]
.
525ca4fb-2d43-46f3-b912-8765f639c46f.config
contents:
abc="Hello World"
exampleProp="123"
felix.fileinstall.filename="file:/..._.cfg"
service.factoryPid="com.example.my.component"
service.pid="com.example.my.component.525ca4fb-2d43-46f3-b912-8765f639c46f"
The 525ca4fb-2d43-46f3-b912-8765f639c46f seems to be some randomly generated ID (possibly UUID).

Resources