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.
Related
I'm writing a Spring Boot application and am trying to load some values from a properties file using the #Value annotation. However, the variables with this annotation remain null even though I believe they should get a value.
The files are located in src/main/resources/custom.propertes and src/main/java/MyClass.java.
(I have removed parts of the code that I believe are irrelevant from the snippets below)
MyClass.java
#Component
#PropertySource("classpath:custom.properties")
public class MyClass {
#Value("${my.property:default}")
private String myProperty;
public MyClass() {
System.out.println(myProperty); // throws NullPointerException
}
}
custom.properties
my.property=hello, world!
What should I do to ensure I can read the values from my property file?
Thanks!
#value will be invoked after the object is created. Since you are using the property inside the constructor hence it is not available.
You should be using constructor injection anyway. It makes testing your class easier.
public MyClass(#Value("${my.property:default}") String myProperty) {
System.out.println(myProperty); // doesn't throw NullPointerException
}
You are getting this error because you are initializing the class with new keyword. To solve this,
first you need to create the configuration class and under this class you need to create the bean of this class.
When you will call it by using bean then it will work..
My code:
#Component
#PropertySource("db.properties")
public class ConnectionFactory {
#Value("${jdbc.user}")
private String user;
#Value("${jdbc.password}")
private String password;
#Value("${jdbc.url}")
private String url;
Connection connection;
#Bean
public String init(){
return ("the value is: "+user);
}
My Config.class:
#Configuration
#ComponentScan
public class Config {
#Bean
public Testing testing() {
return new Testing();
}
#Bean
public ConnectionFactory connectionFactory() {
return new ConnectionFactory();
}
}
Calling it:
public static void main(String[] args) {
AnnotationConfigApplicationContext context= new AnnotationConfigApplicationContext(Config.class);
ConnectionFactory connectionFactory= context.getBean(ConnectionFactory.class);
System.out.println(connectionFactory.init());
}



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;
}
}
I'm trying to use a PropertyReader class to get my information (portNumber, hostName, etc) from a test.properties file in java spring, but I am pretty confused on how to go about this because there seems to be a million different ways (resource bundle etc)... I would greatly appreciate any input! Here's what my code looks like. Oh and the names are the same as in the properties file.
#Configuration
#PropertySource("classpath:test.properties")
public class PropertyReader {
private Environment environment;
String portNumber;
String hostName;
String subjectName;
String myUrl;
String portProtocol;
String hostProtocol;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
public PropertyReader(String propertyFile) throws IOException {...}
I've already added
<context:property-placeholder location="classpath:test.properties" />
to my context. but not sure how to go about getting the info as environment.getProperty doesn't seem to be recognized as a function...
For what it's worth, this is how I do it: https://github.com/KevinWorkman/StaticVoidGames/blob/master/StaticVoidGames/src/main/java/com/StaticVoidGames/spring/config/PropertiesConfig.java
An example PropertiesConfig file:
#Configuration
#PropertySource("classpath:/live.properties")
public class PropertiesConfig {
#Value( "${property.name}" )
private String property;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
And here's how I use those properties from a Controller class: https://github.com/KevinWorkman/StaticVoidGames/blob/master/StaticVoidGames/src/main/java/com/StaticVoidGames/spring/controller/StaticVoidGamesController.java
#Component
public class ExampleController implements ExampleControllerInterface{
#Autowired
private Environment env;
public String viewHomePage(HttpServletRequest request, ModelMap model, HttpSession session){
String property = env.getProperty("property.name");
model.addAttribute("propertyValue", property);
return "index";
}
}
But you're right about there being a million different ways to do things. That's what makes programming so fun- and so frustrating!
I would recomend using Typesafe Configuration Properties, it keeps it litle bit cleaner and you do not need to use to many annotations.
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
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/>.
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 {
// ...
}