Database-driven resource bundle in Spring - spring

I have problem to make "database-driven resource bundle" work. In example below TextDAO is properly injected during application start, but when messageSource is accessed, a new Messages object is created - that's the point. How to make this work ?
<!-- message source -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="someapp.bundle.Messages" />
</bean>
Messages.java
#Component
public class Messages extends ListResourceBundle {
#Autowired
private TextDAO textDAO;
public Messages() {
log.debug("CONSTRUCTOR");
}
#Override
protected Object[][] getContents() {
// loading messages from DB
List<Text> texts = textDAO.findAll(); // textDAO is null
...
}
}
RE-OPEN
As Bozho suggest i did my resource bundle as below, but have problem to dynamically reload it. I suppose that ReloadableResourceBundleMessageSource is for properties files, but maybe it is possible to work this, too.
public class DatabaseDrivenMessageSource extends ReloadableResourceBundleMessageSource {
private Logger log = LoggerFactory.getLogger(getClass());
private final Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();
private TextDAO textDAO;
#Autowired
public DatabaseDrivenMessageSource(TextDAO textDAO) {
this.textDAO = textDAO;
reload();
}
#Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = getText(code, locale);
MessageFormat result = createMessageFormat(msg, locale);
return result;
}
#Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return getText(code, locale);
}
private String getText(String code, Locale locale) {
Map<String, String> localized = properties.get(code);
String textForCurrentLanguage = null;
if (localized != null) {
textForCurrentLanguage = localized.get(locale.getLanguage());
if (textForCurrentLanguage == null) {
textForCurrentLanguage = localized.get(Locale.ENGLISH.getLanguage());
}
}
return textForCurrentLanguage != null ? textForCurrentLanguage : code;
}
public void reload() {
properties.clear();
properties.putAll(loadTexts());
}
protected Map<String, Map<String, String>> loadTexts() {
log.debug("loadTexts");
Map<String, Map<String, String>> m = new HashMap<String, Map<String, String>>();
List<Text> texts = textDAO.findAll();
for(Text text: texts) {
Map<String, String> v = new HashMap<String, String>();
v.put("en", text.getEn());
v.put("de", text.getDe());
m.put(text.getKey(), v);
}
return m;
}
}

basename is a string, so it is not a spring bean, and hence no injection there.
What you can try to do is to subclass ReloadableResourceBundleMessageSource and override some methods there (for example - getMessage(..)). The DAO should be injected in the subclass.

ReloadableResourceBundleMessageSource creates an instance of the class named by the basename property. This instance will not have any of its dependencies injected by Spring. That's why the textDAO field in your Messages object is null.
This Spring issue has the source code for an example JDBC backed MessageSource, as an attachment, which you can use instead of ReloadableResourceBundleMessageSource.

A bit old, but still relevant...
I am using Spring Cloud Config server with JDBC backend as resource bundle for i18n.
Zero code. Works awesome!

Related

Spring Boot KeyCloak Override ClientIdAndSecretCredentialsProvider not working

I am trying to override ClientIdAndSecretCredentialsProvider in KeyCloak Spring Boot. Below is the code I have tried.
public class CustomClientCredentialsProvider extends ClientIdAndSecretCredentialsProvider {
public static final String PROVIDER_ID = CredentialRepresentation.SECRET;
private String clientSecret;
#Override
public String getId() {
return PROVIDER_ID;
}
#Override
public void init(KeycloakDeployment deployment, Object config) {
clientSecret = (String) config;
}
#Override
public void setClientCredentials(KeycloakDeployment deployment, Map<String, String> requestHeaders, Map<String, String> formParams) {
String clientId = deployment.getResourceName();
if (!deployment.isPublicClient()) {
if (clientSecret != null) {
// do something else
}
} else {
formParams.put(OAuth2Constants.CLIENT_ID, clientId);
}
}
}
However even after overwrite, I can still see the control going to the existing Spring Boot's ClientIdAndSecretCredentialsProvider rather than mine.
How can I get the control to mine rather than Spring Boot's? Is there something else that needs to be set??
I believe you have to pass an instance of CustomClientCredentialsProvider to the Adapter Deployment.
See AdapterDeploymentContext#setClientAuthenticator(ClientCredentialsProvider clientAuthenticator)

Hibernate Anntotaion #Converter need to be configure before Spring Anntotation #propertySource

I am using #converter (Hibernate )to convert pojo in encrypted format which is from hibernate but key are placed in property file which would not be resolve by #propertySource (Spring annotation)
is there any way to manage bean creation seq in above case.
Please find the below code snippet for Converter, I had created another bean from encryption/decryption, but you can create config bean for properties and read properties from there.
#Component
#Converter
#Configurable
public class HashMapConverter implements AttributeConverter<Map<String, Object>, String> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/*
* Define your application properties bean here and read the properties from
* there
*/
private static ConfigEncryptionKeyConverter configEncryptionKeyConverter;
#Autowired
public void initEncryptionKeyConverter(ConfigEncryptionKeyConverter configEncryptionKeyConverter) {
// Set your beans here.
HashMapConverter.configEncryptionKeyConverter = configEncryptionKeyConverter;
}
#Override
public String convertToDatabaseColumn(Map<String, Object> attribute) {
try {
return configEncryptionKeyConverter.convertToDatabaseColumn(OBJECT_MAPPER.writeValueAsString(attribute));
} catch (final JsonProcessingException e) {
throw new ApplicationErrorException(e.getLocalizedMessage());
}
}
#SuppressWarnings("unchecked")
#Override
public Map<String, Object> convertToEntityAttribute(String dbData) {
Map<String, Object> attribute = null;
if (dbData != null) {
try {
attribute = OBJECT_MAPPER.readValue(configEncryptionKeyConverter.convertToEntityAttribute(dbData),
Map.class);
} catch (final IOException e) {
throw new ApplicationErrorException(e.getLocalizedMessage());
}
}
return attribute;
}
}
Hope this will help.

How to implement multitenancy for redis in spring boot

I am working on making my whole application multi-tenanted but stuck on redis. So far I created a map of JedisConnectionFactory and tried to pass it to RedisTemplate but it throwing java.lang.IllegalArgumentException: template not initialized; call afterPropertiesSet() before using it.
Below are code snippets:
#Component
public class RedisConfiguration {
#Autowired
private DSConfig dsConfig;
private Map<String,JedisConnectionFactory> jedisConnectionFactoryMap = new HashMap<>();
private static Logger LOGGER = LoggerFactory.getLogger(RedisConfiguration.class);
#PostConstruct
public void initializeJedisConnectionFactories() {
for(DatasourceDetail datasourceDetail : dsConfig.getDatasources()) {
RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
redisConfig.setHostName(datasourceDetail.getRedisHost());
redisConfig.setPassword(RedisPassword.of(datasourceDetail.getRedisPassword()));
redisConfig.setPort(Integer.parseInt(datasourceDetail.getRedisPort()));
JedisClientConfiguration configuration = JedisClientConfiguration.builder().usePooling().
poolConfig(new JedisPoolConfig()).build();
jedisConnectionFactoryMap.put(datasourceDetail.getTenantId()
,new JedisConnectionFactory(redisConfig,configuration));
}
LOGGER.info("Connection factory count " + jedisConnectionFactoryMap.size());
}
public RedisTemplate< String, Object > redisTemplate() throws Exception {
final RedisTemplate< String, Object > template = new RedisTemplate< String, Object >();
template.setConnectionFactory( jedisConnectionFactory() );
template.setKeySerializer( new StringRedisSerializer() );
template.setHashValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
template.setValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
return template;
}
JedisConnectionFactory jedisConnectionFactory() throws Exception {
if(TenantContext.getCurrentTenant()==null) {
throw new Exception("No tenant context found");
}
LOGGER.info("Returning redis connection for tenant" + TenantContext.getCurrentTenant());
return jedisConnectionFactoryMap.get(TenantContext.getCurrentTenant());
}
}
And I am using redis template as below:
#CrossOrigin("*")
#RestController
#RequestMapping("/redis")
public class RedisController {
#Autowired
private RedisConfiguration redisConfiguration;
#GetMapping("/set")
#ResponseBody
public Object set(#RequestParam(value = "key", required = true) String key,
#RequestParam(value = "value", required = true) String value) throws Exception {
redisConfiguration.redisTemplate().opsForValue().set( key,value );
return true;
}
#GetMapping("/get")
#ResponseBody
public Object get(#RequestParam(value = "key", required = true) String key) throws Exception {
redisConfiguration.redisTemplate().opsForValue().get(key);
return true;
}
}
Is there anyway I can implement this in a better way or is there any other way which spring redis provides?
Finally I was able to resolve by calling afterPropertiesSet() explicitly.

spring boot and freemarker configure TemplateDirectiveModel and TemplateMethodModelEx

I have a project built with Spring Boot + FreeMarker, and it worked fine until tonight, but I don't think I had changed anything; however it failed. Below is my FreeMarker configuration class:
#Configuration
#Slf4j
public class FreemarkerConfiguration extends FreeMarkerAutoConfiguration.FreeMarkerWebConfiguration {
/**
* autowired all implementations of freemarker.template.TemplateDirectiveModel
*/
#Autowired
Map<String, TemplateDirectiveModel> directiveModelMap;
/**
* autowired all implementations of freemarker.template.TemplateMethodModelEx
*/
#Autowired
Map<String, TemplateMethodModelEx> methodModelExMap;
private static final String CUSTOM_DIRECTIVE_SUFFIX = "Directive";
private static final String CUSTOM_METHOD_SUFFIX = "Method";
#Override
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = super.freeMarkerConfigurer();
Map<String, Object> sharedVariables = new HashMap<String, Object>();
if (!CollectionUtils.isEmpty(directiveModelMap)) {
Map<String, Object> map = new HashMap<String, Object>();
for (Map.Entry<String, TemplateDirectiveModel> entry : directiveModelMap.entrySet()) {
map.put(StringUtils.uncapitalize(entry.getKey()).replaceAll(CUSTOM_DIRECTIVE_SUFFIX, ""), entry.getValue());
}
sharedVariables.putAll(map);
}
if (!CollectionUtils.isEmpty(this.methodModelExMap)) {
Map<String, Object> map = new HashMap<String, Object>();
for (Map.Entry<String, TemplateMethodModelEx> entry : this.methodModelExMap.entrySet()) {
map.put(StringUtils.uncapitalize(entry.getKey()).replaceAll(CUSTOM_METHOD_SUFFIX, ""), entry.getValue());
}
sharedVariables.putAll(map);
}
BeansWrapper beansWrapper = new BeansWrapperBuilder(freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build();
sharedVariables.put("enums", beansWrapper.getEnumModels());
configurer.setFreemarkerVariables(sharedVariables);
return configurer;
}
}
Problem is that
#Autowired
Map<String, TemplateDirectiveModel> directiveModelMap;
#Autowired
Map<String, TemplateMethodModelEx> methodModelExMap;
I want to inject all implementations of TemplateDirectiveModel and TemplateMethodModelEx , but both Map<String ,TemplateDirectiveModel/TemplateMethodModelEx> got null. Of course, the implementations annotated with #Compoment. I don't know why, I compared the diffs but got no answers, why the Maps instantiated after
#Override
public FreeMarkerConfigurer freeMarkerConfigurer(){ .... }
Here's my boot application
#Configuration
#SpringBootApplication
#EntityScan("com.hmxx.entity")
#EnableAspectJAutoProxy
#EnableTransactionManagement
#EnableJpaRepositories(value = {"com.hmxx.service"})
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(new Object[]{Application.class});
app.setWebEnvironment(true);
//app.setBannerMode(Banner.Mode.CONSOLE);
ConfigurableApplicationContext ctx = app.run(args);
Map<String, TemplateDirectiveModel> directiveModelMap = ctx.getBeansOfType(TemplateDirectiveModel.class);
Map<String, TemplateMethodModelEx> methodModelExMap = ctx.getBeansOfType(TemplateMethodModelEx.class);
}
#Autowired
DataInitService dataInitService;
#Override
public void run(String... args) throws Exception {
// dataInitService.initAdminUser();
}
}
And obviously Map<String, TemplateDirectiveModel>、Map<String, TemplateMethodModelEx> methodModelExMap both not null.
I want to know why the injection got null and hope to resolve it.

Access properties file programmatically with Spring?

We use the code below to inject Spring beans with properties from a properties file.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:/my.properties"/>
</bean>
<bean id="blah" class="abc">
<property name="path" value="${the.path}"/>
</bean>
Is there a way we can access the properties programmatically? I'm trying to do some code without dependency injection. So I'd like to just have some code like this:
PropertyPlaceholderConfigurer props = new PropertyPlaceholderConfigurer();
props.load("classpath:/my.properties");
props.get("path");
How about PropertiesLoaderUtils?
Resource resource = new ClassPathResource("/my.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
If all you want to do is access placeholder value from code, there is the #Value annotation:
#Value("${settings.some.property}")
String someValue;
To access placeholders From SPEL use this syntax:
#('${settings.some.property}')
To expose configuration to views that have SPEL turned off, one can use this trick:
package com.my.app;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.stereotype.Component;
#Component
public class PropertyPlaceholderExposer implements Map<String, String>, BeanFactoryAware {
ConfigurableBeanFactory beanFactory;
#Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
protected String resolveProperty(String name) {
String rv = beanFactory.resolveEmbeddedValue("${" + name + "}");
return rv;
}
#Override
public String get(Object key) {
return resolveProperty(key.toString());
}
#Override
public boolean containsKey(Object key) {
try {
resolveProperty(key.toString());
return true;
}
catch(Exception e) {
return false;
}
}
#Override public boolean isEmpty() { return false; }
#Override public Set<String> keySet() { throw new UnsupportedOperationException(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { throw new UnsupportedOperationException(); }
#Override public Collection<String> values() { throw new UnsupportedOperationException(); }
#Override public int size() { throw new UnsupportedOperationException(); }
#Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
And then use the exposer to expose properties to a view:
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver" id="tilesViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
<property name="attributesMap">
<map>
<entry key="config">
<bean class="com.my.app.PropertyPlaceholderExposer" />
</entry>
</map>
</property>
</bean>
Then in view, use the exposed properties like this:
${config['settings.some.property']}
This solution has the advantage that you can rely on standard placeholder
implementation injected by the context:property-placeholder tag.
Now as a final note, if you really need a to capture all placeholder properties and their values, you have to pipe them through StringValueResolver to make sure that placeholders work inside the property values as expected. The following code will do that.
package com.my.app;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.StringValueResolver;
public class AppConfig extends PropertyPlaceholderConfigurer implements Map<String, String> {
Map<String, String> props = new HashMap<String, String>();
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
this.props.clear();
for (Entry<Object, Object> e: props.entrySet())
this.props.put(e.getKey().toString(), e.getValue().toString());
super.processProperties(beanFactory, props);
}
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
super.doProcessProperties(beanFactoryToProcess, valueResolver);
for(Entry<String, String> e: props.entrySet())
e.setValue(valueResolver.resolveStringValue(e.getValue()));
}
// Implement map interface to access stored properties
#Override public Set<String> keySet() { return props.keySet(); }
#Override public Set<java.util.Map.Entry<String, String>> entrySet() { return props.entrySet(); }
#Override public Collection<String> values() { return props.values(); }
#Override public int size() { return props.size(); }
#Override public boolean isEmpty() { return props.isEmpty(); }
#Override public boolean containsValue(Object value) { return props.containsValue(value); }
#Override public boolean containsKey(Object key) { return props.containsKey(key); }
#Override public String get(Object key) { return props.get(key); }
#Override public void clear() { throw new UnsupportedOperationException(); }
#Override public String put(String key, String value) { throw new UnsupportedOperationException(); }
#Override public String remove(Object key) { throw new UnsupportedOperationException(); }
#Override public void putAll(Map<? extends String, ? extends String> t) { throw new UnsupportedOperationException(); }
}
I have done this and it has worked.
Properties props = PropertiesLoaderUtils.loadAllProperties("my.properties");
PropertyPlaceholderConfigurer props2 = new PropertyPlaceholderConfigurer();
props2.setProperties(props);
That should work.
CREDIT: Programmatic access to properties in Spring without re-reading the properties file
I've found a nice implementation of accessing the properties programmatically in spring without reloading the same properties that spring has already loaded. [Also, It is not required to hardcode the property file location in the source]
With these changes, the code looks cleaner & more maintainable.
The concept is pretty simple. Just extend the spring default property placeholder (PropertyPlaceholderConfigurer) and capture the properties it loads in the local variable
public class SpringPropertiesUtil extends PropertyPlaceholderConfigurer {
private static Map<String, String> propertiesMap;
// Default as in PropertyPlaceholderConfigurer
private int springSystemPropertiesMode = SYSTEM_PROPERTIES_MODE_FALLBACK;
#Override
public void setSystemPropertiesMode(int systemPropertiesMode) {
super.setSystemPropertiesMode(systemPropertiesMode);
springSystemPropertiesMode = systemPropertiesMode;
}
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = resolvePlaceholder(keyStr, props, springSystemPropertiesMode);
propertiesMap.put(keyStr, valueStr);
}
}
public static String getProperty(String name) {
return propertiesMap.get(name).toString();
}
}
Usage Example
SpringPropertiesUtil.getProperty("myProperty")
Spring configuration changes
<bean id="placeholderConfigMM" class="SpringPropertiesUtil">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:myproperties.properties</value>
</list>
</property>
</bean>
Hope this helps to solve the problems you have
You can also use either the spring utils, or load properties via the PropertiesFactoryBean.
<util:properties id="myProps" location="classpath:com/foo/myprops.properties"/>
or:
<bean id="myProps" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:com/foo/myprops.properties"/>
</bean>
Then you can pick them up in your application with:
#Resource(name = "myProps")
private Properties myProps;
and additionally use these properties in your config:
<context:property-placeholder properties-ref="myProps"/>
This is also in the docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#xsd-config-body-schemas-util-properties
Create a class like below
package com.tmghealth.common.util;
import java.util.Properties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#Configuration
#PropertySource(value = { "classpath:/spring/server-urls.properties" })
public class PropertiesReader extends PropertyPlaceholderConfigurer {
#Override
protected void processProperties(
ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException {
super.processProperties(beanFactory, props);
}
}
Then wherever you want to access a property use
#Autowired
private Environment environment;
and getters and setters then access using
environment.getProperty(envName
+ ".letter.fdi.letterdetails.restServiceUrl");
-- write getters and setters in the accessor class
public Environment getEnvironment() {
return environment;
}`enter code here`
public void setEnvironment(Environment environment) {
this.environment = environment;
}
You can get your properties through Environment class. As documentation stands:
Properties play an important role in almost all applications, and may originate from a variety of sources: properties files, JVM system properties, system environment variables, JNDI, servlet context parameters, ad-hoc Properties objects, Maps, and so on. The role of the environment object with relation to properties is to provide the user with a convenient service interface for configuring property sources and resolving properties from them.
Having Environment as a env variable, simply call:
env.resolvePlaceholders("${your-property:default-value}")
You can get your 'raw' properties through:
env.getProperty("your-property")
It will search through all properties source that spring has registered.
You can either obtain Environment through:
inject ApplicationContext by implementing ApplicationContextAware and then call getEnvironment() on context
implement EnvironmentAware.
It's obtain through implementation of a class because properties are resolved on early stage of application startup, as they may be required for bean construction.
Read more on documentation: spring Environment documentation
As you know the newer versions of Spring don't use the PropertyPlaceholderConfigurer and now use another nightmarish construct called PropertySourcesPlaceholderConfigurer. If you're trying to get resolved properties from code, and wish the Spring team gave us a way to do this a long time ago, then vote this post up! ... Because this is how you do it the new way:
Subclass PropertySourcesPlaceholderConfigurer:
public class SpringPropertyExposer extends PropertySourcesPlaceholderConfigurer {
private ConfigurableListableBeanFactory factory;
/**
* Save off the bean factory so we can use it later to resolve properties
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
super.processProperties(beanFactoryToProcess, propertyResolver);
if (beanFactoryToProcess.hasEmbeddedValueResolver()) {
logger.debug("Value resolver exists.");
factory = beanFactoryToProcess;
}
else {
logger.error("No existing embedded value resolver.");
}
}
public String getProperty(String name) {
Object propertyValue = factory.resolveEmbeddedValue(this.placeholderPrefix + name + this.placeholderSuffix);
return propertyValue.toString();
}
}
To use it, make sure to use your subclass in your #Configuration and save off a reference to it for later use.
#Configuration
#ComponentScan
public class PropertiesConfig {
public static SpringPropertyExposer commonEnvConfig;
#Bean(name="commonConfig")
public static PropertySourcesPlaceholderConfigurer commonConfig() throws IOException {
commonEnvConfig = new SpringPropertyExposer(); //This is a subclass of the return type.
PropertiesFactoryBean commonConfig = new PropertiesFactoryBean();
commonConfig.setLocation(new ClassPathResource("META-INF/spring/config.properties"));
try {
commonConfig.afterPropertiesSet();
}
catch (IOException e) {
e.printStackTrace();
throw e;
}
commonEnvConfig.setProperties(commonConfig.getObject());
return commonEnvConfig;
}
}
Usage:
Object value = PropertiesConfig.commonEnvConfig.getProperty("key.subkey");
This help me:
ApplicationContextUtils.getApplicationContext().getEnvironment()
Here is another sample .
XmlBeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
cfg.postProcessBeanFactory(factory);
This will resolve any nested properties.
public class Environment extends PropertyPlaceholderConfigurer {
/**
* Map that hold all the properties.
*/
private Map<String, String> propertiesMap;
/**
* Iterate through all the Property keys and build a Map, resolve all the nested values before building the map.
*/
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props) throws BeansException {
super.processProperties(beanFactory, props);
propertiesMap = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
String valueStr = beanFactory.resolveEmbeddedValue(placeholderPrefix + keyStr.trim() + DEFAULT_PLACEHOLDER_SUFFIX);
propertiesMap.put(keyStr, valueStr);
}
}
/**
* This method gets the String value for a given String key for the property files.
*
* #param name - Key for which the value needs to be retrieved.
* #return Value
*/
public String getProperty(String name) {
return propertiesMap.get(name).toString();
}
This post also explatis howto access properties: http://maciej-miklas.blogspot.de/2013/07/spring-31-programmatic-access-to.html
You can access properties loaded by spring property-placeholder over such spring bean:
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
This is the finest way I got it to work:
package your.package;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
public class ApplicationProperties {
private Properties properties;
public ApplicationProperties() {
// application.properties located at src/main/resource
Resource resource = new ClassPathResource("/application.properties");
try {
this.properties = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException ex) {
Logger.getLogger(ApplicationProperties.class.getName()).log(Level.SEVERE, null, ex);
}
}
public String getProperty(String propertyName) {
return this.properties.getProperty(propertyName);
}
}
create .properties file in classpath of your project and add path configuration in xml`<context:property-placeholder location="classpath*:/*.properties" />`
in servlet-context.xml after that u can directly use your file everywhere
Please use the below code in your spring configuration file to load the file from class path of your application
<context:property-placeholder
ignore-unresolvable="true" ignore-resource-not-found="false" location="classpath:property-file-name" />
I know this is an old thread, however, this topic in my opinion becomes of great importance for those using the functional approach for all those usecases where you need a microservice that loads "instantly" and therefore you avoid using annotations.
The problem that remained unsolved was to load eventually the environment variables which I had in my application.yml.
public class AppPropsLoader {
public static Properties load() {
var propPholderConfig = new PropertySourcesPlaceHolderConfigurer();
var yaml = new YamlPropertiesFactoryBean();
ClassPathResource resource = new ClassPathResource("application.yml");
Objects.requireNonNull(resource, "File application.yml does not exist");
yaml.setResources(resource);
Objects.requireNonNull(yaml.getObject(), "Configuration cannot be null");
propPholderConfig.postProcessBeanFactory(new DefaultListableBeanFactory());
propPholderConfig.setProperties(yaml.getObject());
PropertySources appliedPropertySources =
propPholderConfig.getAppliedPropertySources();
var resolver = new PropertySourcesPlaceholderResolver(appliedPropertySources);
Properties resolvedProps = new Properties();
for (Map.Entry<Object, Object> prop: yaml.getObject().entrySet()) {
resolvedProps.setProperty((String)prop.getKey(),
getPropertyValue(resolver.resolvePlaceHolders(prop.getValue()));
}
return resolvedProps;
}
static String getPropertyValue(Object prop) {
var val = String.valueOf(prop);
Pattern p = Pattern.compile("^(\\$\\{)([a-zA-Z0-9-._]+)(\\})$");
Matcher m = p.matcher(val);
if(m.matches()) {
return System.getEnv(m.group(2));
}
return val;
}
}

Resources