classpath wildcard in #PropertySource - java

I am using Spring Java config to create my bean.
But this bean is common to 2 applications.
Both have one property file abc.properties but with different classpath locations.
When i put explicit classpath like
#PropertySource("classpath:/app1/abc.properties")
then it works but when i try to use wildcard like
#PropertySource("classpath:/**/abc.properties")
then it doesn't work.
I try many combinations of wildcard but it still not working.
Is wildcard works in #ProeprtySource
Is there any other way to read to property in classed marked with #Configurations.

#PropertySource API: Resource location wildcards (e.g. **/*.properties) are not permitted; each location must evaluate to exactly one .properties resource.
workaround: try
#Configuration
public class Test {
#Bean
public PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer()
throws IOException {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/**/abc.properties"));
return ppc;
}

Addidtionally to dmay workaround:
Since Spring 3.1 PropertySourcesPlaceholderConfigurer should be used preferentially over PropertyPlaceholderConfigurer and the bean should be static.
#Configuration
public class PropertiesConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer propertyConfigurer = new PropertySourcesPlaceholderConfigurer();
propertyConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/**/abc.properties"));
return propertyConfigurer;
}
}

If you're using YAML properties, this can be achieved using a custom PropertySourceFactory:
public class YamlPropertySourceFactory implements PropertySourceFactory {
private static final Logger logger = LoggerFactory.getLogger(YamlPropertySourceFactory.class);
#Override
#NonNull
public PropertySource<?> createPropertySource(
#Nullable String name,
#NonNull EncodedResource encodedResource
) {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
String path = ((ClassPathResource) encodedResource.getResource()).getPath();
String filename = encodedResource.getResource().getFilename();
Properties properties;
try {
factory.setResources(
new PathMatchingResourcePatternResolver().getResources(path)
);
properties = Optional.ofNullable(factory.getObject()).orElseGet(Properties::new);
return new PropertiesPropertySource(filename, properties);
} catch (Exception e) {
logger.error("Properties not configured correctly for {}", path, e);
return new PropertiesPropertySource(filename, new Properties());
}
}
}
Usage:
#PropertySource(value = "classpath:**/props.yaml", factory = YamlPropertySourceFactory.class)
#SpringBootApplication
public class MyApplication {
// ...
}

Related

How does spring boot autoconfigure works if the class is not present in class path

I was look at the Kafka Auto configure in spring-boot.
Reading the artile Spring-boot auto-configure
The below is the code copied from github Here.
My question is if the jar containing KafkaTemplate.class not present in class path, how does the spring is manage to execute the below code while auto configuring. Since the class is not present should not it lead to Error? or does the spring won't execute this at all while auto-configuring?
#Configuration
#ConditionalOnClass(KafkaTemplate.class)
#EnableConfigurationProperties(KafkaProperties.class)
#Import({ KafkaAnnotationDrivenConfiguration.class,
KafkaStreamsAnnotationDrivenConfiguration.class })
public class KafkaAutoConfiguration {
private final KafkaProperties properties;
private final RecordMessageConverter messageConverter;
public KafkaAutoConfiguration(KafkaProperties properties,
ObjectProvider<RecordMessageConverter> messageConverter) {
this.properties = properties;
this.messageConverter = messageConverter.getIfUnique();
}
#Bean
#ConditionalOnMissingBean(KafkaTemplate.class)
public KafkaTemplate<?, ?> kafkaTemplate(
ProducerFactory<Object, Object> kafkaProducerFactory,
ProducerListener<Object, Object> kafkaProducerListener) {
KafkaTemplate<Object, Object> kafkaTemplate = new KafkaTemplate<>(
kafkaProducerFactory);
if (this.messageConverter != null) {
kafkaTemplate.setMessageConverter(this.messageConverter);
}
kafkaTemplate.setProducerListener(kafkaProducerListener);
kafkaTemplate.setDefaultTopic(this.properties.getTemplate().getDefaultTopic());
return kafkaTemplate;
}
#Bean
#ConditionalOnMissingBean(ProducerListener.class)
public ProducerListener<Object, Object> kafkaProducerListener() {
return new LoggingProducerListener<>();
}
#Bean
#ConditionalOnMissingBean(ConsumerFactory.class)
public ConsumerFactory<?, ?> kafkaConsumerFactory() {
return new DefaultKafkaConsumerFactory<>(
this.properties.buildConsumerProperties());
}
#Bean
#ConditionalOnMissingBean(ProducerFactory.class)
public ProducerFactory<?, ?> kafkaProducerFactory() {
DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory<>(
this.properties.buildProducerProperties());
String transactionIdPrefix = this.properties.getProducer()
.getTransactionIdPrefix();
if (transactionIdPrefix != null) {
factory.setTransactionIdPrefix(transactionIdPrefix);
}
return factory;
}
#Bean
#ConditionalOnProperty(name = "spring.kafka.producer.transaction-id-prefix")
#ConditionalOnMissingBean
public KafkaTransactionManager<?, ?> kafkaTransactionManager(
ProducerFactory<?, ?> producerFactory) {
return new KafkaTransactionManager<>(producerFactory);
}
#Bean
#ConditionalOnProperty(name = "spring.kafka.jaas.enabled")
#ConditionalOnMissingBean
public KafkaJaasLoginModuleInitializer kafkaJaasInitializer() throws IOException {
KafkaJaasLoginModuleInitializer jaas = new KafkaJaasLoginModuleInitializer();
Jaas jaasProperties = this.properties.getJaas();
if (jaasProperties.getControlFlag() != null) {
jaas.setControlFlag(jaasProperties.getControlFlag());
}
if (jaasProperties.getLoginModule() != null) {
jaas.setLoginModule(jaasProperties.getLoginModule());
}
jaas.setOptions(jaasProperties.getOptions());
return jaas;
}
#Bean
#ConditionalOnMissingBean
public KafkaAdmin kafkaAdmin() {
KafkaAdmin kafkaAdmin = new KafkaAdmin(this.properties.buildAdminProperties());
kafkaAdmin.setFatalIfBrokerNotAvailable(this.properties.getAdmin().isFailFast());
return kafkaAdmin;
}
}
Thanks for help
Referring from spring boot documentation:
The #ConditionalOnClass and #ConditionalOnMissingClass annotations let #Configuration classes be included based on the presence or absence of specific classes. Due to the fact that annotation metadata is parsed by using ASM, you can use the value attribute to refer to the real class, even though that class might not actually appear on the running application classpath. You can also use the name attribute if you prefer to specify the class name by using a String value.
This mechanism does not apply the same way to #Bean methods where typically the return type is the target of the condition: before the condition on the method applies, the JVM will have loaded the class and potentially processed method references which will fail if the class is not present
To answer your question, spring doesn't even include KafkaAutoConfiguration class.

How to read data from java properties file using Spring Boot

I have a spring boot application and I want to read some variable from my application.properties file. In fact below codes do that. But I think there is a good method for this alternative.
Properties prop = new Properties();
InputStream input = null;
try {
input = new FileInputStream("config.properties");
prop.load(input);
gMapReportUrl = prop.getProperty("gMapReportUrl");
} catch (IOException ex) {
ex.printStackTrace();
} finally {
...
}
You can use #PropertySource to externalize your configuration to a properties file. There is number of way to do get properties:
1.
Assign the property values to fields by using #Value with PropertySourcesPlaceholderConfigurer to resolve ${} in #Value:
#Configuration
#PropertySource("file:config.properties")
public class ApplicationConfiguration {
#Value("${gMapReportUrl}")
private String gMapReportUrl;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
2.
Get the property values by using Environment:
#Configuration
#PropertySource("file:config.properties")
public class ApplicationConfiguration {
#Autowired
private Environment env;
public void foo() {
env.getProperty("gMapReportUrl");
}
}
Hope this can help
I have created following class
ConfigUtility.java
#Configuration
public class ConfigUtility {
#Autowired
private Environment env;
public String getProperty(String pPropertyKey) {
return env.getProperty(pPropertyKey);
}
}
and called as follow to get application.properties value
myclass.java
#Autowired
private ConfigUtility configUtil;
public AppResponse getDetails() {
AppResponse response = new AppResponse();
String email = configUtil.getProperty("emailid");
return response;
}
application.properties
emailid=sunny#domain.com
unit tested, working as expected...
i would suggest the following way:
#PropertySource(ignoreResourceNotFound = true, value = "classpath:otherprops.properties")
#Controller
public class ClassA {
#Value("${myName}")
private String name;
#RequestMapping(value = "/xyz")
#ResponseBody
public void getName(){
System.out.println(name);
}
}
Here your new properties file name is "otherprops.properties" and the property name is "myName". This is the simplest implementation to access properties file in spring boot version 1.5.8.

IllegalArgumentException: Invalid boolean value when injecting property to boolean in Spring

In Spring Boot I attempt to inject a boolean value to an instance variable from an enviroment property which is set (enabled=true).
#Value("${enabled}")
private boolean enabled;
However Spring cannot resolve this for some reason and reports:
Caused by: java.lang.IllegalArgumentException: Invalid boolean value [${enabled}]
It seems like it does not replace expression ${enabled} with the property value.
What needs to be set ? Why doesn't work this by default ?
For Spring-Boot you can find good reference here:
http://www.baeldung.com/properties-with-spring
For given someprops.properites file:
somevalue:true
Here is FAILING version:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#TestPropertySource( "/someprops.properties" )
public class FailingNg {
#Configuration
public static class Config {
}
#Value("${somevalue}")
private Boolean somevalue;
// Fails with context initializatoin java.lang.IllegalArgumentException: Invalid boolean value [${somevalue}]
#Test
public void test1() {
System.out.println("somevalue: " + somevalue);
}
}
Here is WORKING version:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class WorkingOk {
#Configuration
public static class Config {
#Bean
public static PropertyPlaceholderConfigurer properties() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] {
new ClassPathResource( "someprops.properties" )
};
ppc.setLocations( resources );
ppc.setIgnoreUnresolvablePlaceholders( true );
return ppc;
}
}
#Value("${somevalue}")
private Boolean somevalue;
#Test
public void test1() {
System.out.println("somevalue: " + somevalue);
}
}
As mentioned in the comments, you are likely missing a PropertySourcesPlaceholderConfigurer bean definition. Here's an example of how to configure that within Java:
#Configuration
public class PropertyConfig {
private static final String PROPERTY_FILENAME = "app.properties"; // Change as needed.
/**
* This instance is necessary for Spring to load up the property file and allow access to
* it through the #Value(${propertyName}) annotation. Also note that this bean must be static
* in order to work properly with current Spring behavior.
*/
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[] { new ClassPathResource(PROPERTY_FILENAME) };
pspc.setLocations(resources);
pspc.setIgnoreUnresolvablePlaceholders(true);
return pspc;
}
}

Cannot access properties

I got a Spring MVC application. It runs over the Tomcat 7 server.
I want to make a props.properties file, so my app could access properties during Beans initialization process.
So i did the following:
1.Create a context-parameter to my web.xml
<context-param>
<param-name>mainProps</param-name>
<param-value>${catalina.home}/conf/props.properties</param-value>
</context-param>
2. I created a MainCobfig class
#Configuration
#PropertySource("classpath:/racoonsoft/wish/properties/props.properties")
#Import({WebConfig.class })
public class MainConfig {
#Autowired
Environment env;
#Value("${db.host}")
static String dbHost;
#Value("${db.name}")
static String dbName;
#Value("${db.login}")
static String dbLogin;
#Value("${db.password}")
static String dbPassword;
#Value("${ozon.login}")
static String ozonLogin;
#Value("${ozon.password}")
static String ozonPassword;
#Value("${ozon.apiurl}")
static String ozonApiUrl;
#Value("${payture.apihost}")
static String paytureApiHost;
#Value("${payture.merchant}")
static String paytureMerchant;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
#Bean
public OzonProcessor apiProcessor() {
return new OzonProcessor(ozonLogin, ozonPassword, ozonApiUrl);
}
#Bean
public PGSQLDataSource pgsqlDataSource() throws Exception{
PGSQLDataSource result = new PGSQLDataSource(dbHost,dbName,5432,dbLogin,dbPassword,"org.postgresql.Driver","jdbc:postgresql:");
result.loadSettings();
if(FacebookController.dbProc==null)
{
FacebookController.dbProc = result;
}
//FacebookController.dbProc = result;
return result;
}
#Bean
public PaytureProcessor paytureProcessor()
{
PaytureProcessor proc = new PaytureProcessor(paytureApiHost,paytureMerchant);
return proc;
}
}
3 - I created props.properties file and put it into /conf directory
When i start my application it didnt throw the exception (file not found) - so i beleave it sees the properties file. But during bean initialization my fields (dbHost,dbLogin etc.) are still null`s.
How can i put values from properties file to my fields?
Help me please.
The annotated factory method of PropertySourcesPlaceholderConfigurer MUST be a static method:
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Spring API Reference manual of #Bean remarks this.
A little bit more detailed explanation:
This is because PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor (BFPP).
BFPP does a post-processing on the bean factory just before other (normal) beans are being instantiated and intialized. So, BFPP's are needed to be created in order to work, before the MainConfig bean is instantiated. Marking this factory method as a static method, we can invoke this method without instantiating MainConfig.
# form user login properties
userName.required = User Name is required
In controller side you declare only userName.required. That's how you declare it.

#Value annotation not being properly set

Here's my app:
public static void main( String[] args ) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
//run the importer
final ImportNewOrders importer = (ImportNewOrders) ApplicationContextProvider.getApplicationContext().getBean("importNewOrders");
importer.run();
//importer.runInBackground();
}
Here's my config:
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value = {
"classpath:/application.properties",
"classpath:/environment-${MY_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.fettergroup.production.repositories")
#EnableTransactionManagement
public class Config {
.... skipping things that aren't relevant
#Bean
public ImportNewOrders importNewOrders() {
return new ImportNewOrders();
}
Here's my class...
#Component
public class ImportNewOrders implements Task {
private final static Logger logger = Logger.getLogger(ImportNewOrders.class.getName());
#Autowired
private OrderService orderService;
#Autowired
private ImportOrderRequest importOrderRequest;
#Value("${api.user}")
private String apiUser;
#Value("${api.password}")
private String apiPassword;
#Value("${api.orders.pingFrequency}")
private String pingFrequency;
And finally the application.properties:
# ------------------- Application settings -------------------
#Base URL to the API application
api.baseUrl=http://localhost:9998
#Unique token used to authenticate this vendor
api.vendor.token=asdf
#API credentials
api.user=myuser
api.password=mypassword
#How often to check for new orders; frequency is in seconds
api.orders.pingFrequency=60
This worked an hour or two ago, now it's decided it doesn't like these values. I'm at a loss as to why. Everything looks correct to me.
Update
#Configuration
#ComponentScan(basePackages = {
"com.production"
})
#PropertySource(value = {
"classpath:/application.properties",
"classpath:/environment-${MY_ENVIRONMENT}.properties"
})
#EnableJpaRepositories("com.production.repositories")
#EnableTransactionManagement
public class Config {
#Value("${db.url}")
private static String PROPERTY_DATABASE_URL;
#Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(PROPERTY_DATABASE_URL); //is null
/*dataSource.setUser(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USER));
dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));*/
return dataSource;
}
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer () {
return new PropertySourcesPlaceholderConfigurer();
}
}
Your properties file is found by your #Configuration and is using it for your database properties within that class because of #PropertySource. But #Value fields and ${} evaluation need more than that.
From Javadoc for #PropertySource
In order to resolve ${...} placeholders in definitions or
#Value annotations using properties from a PropertySource, one must
register a PropertySourcesPlaceholderConfigurer. This happens
automatically when using in XML, but
must be explicitly registered using a static #Bean method when using
#Configuration classes. See the "Working with externalized values"
section of #Configuration Javadoc and "a note on
BeanFactoryPostProcessor-returning #Bean methods" of #Bean Javadoc for
details and examples.
So declare a
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
p.setLocation(new ClassPathResource("your properties path"));
// other properties
return p;
}
in your config class, or as ach has aptly mentioned in the comments if you use #PropertySource your can omit setLocation altogether:
#Configuration
#PropertySource(value="classpath:your_file.properties")
public class MyConfiguration{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
return p;
}
}
You shouldn't need the environment when you have the PropertySourcesPlaceholderConfigurer
In most cases, however, application-level beans should not need to>
interact with the Environment directly but instead may have to have
${...} property values replaced by a property placeholder configurer
such as PropertySourcesPlaceholderConfigurer, which itself is
EnvironmentAware and as of Spring 3.1 is registered by default when
using < context:property-placeholder/>.

Categories

Resources