Spring property initialization - java

I have an xml configurable Spring context with the following property placeholders:
<context:property-placeholder
properties-ref="dbProperties" location="classpath:logmessages.properties" order="2"/>
<context:property-placeholder location="classpath:application.properties" order="1"/>
DB properties configuration bean is as follows:
<bean id="dbProperties"
class="com.example.DatabasePropertiesLoader">
<property name="path" value="${db.path}"/>
</bean>
As it comes from the name, this bean loads some properties from the database, for example endpoints and credentials to other services. However, to access the database that keeps this properties it also needs credentials, which are kept in application.properties:
public class DatabasePropertiesLoader extends AbstractFactoryBean<Properties> {
private String path;
#Override
protected Properties createInstance() throws Exception {
// logic loading properties
}
#Override
public Class<Properties> getObjectType() {
return Properties.class;
}
}
Path property kept in application.properties file:
db.path=localhost:7777
As you see, this bean requires "path" property to be injected to be created.
However, it can't be done, because injected value is null. I guess that Spring only knows about application.properties file, not about its contents. Is there any way to solve this?

You need to tell spring to use the properties as below.
#Value("${db.path}")
private String path;

Related

from Spring context to Spring annotation

I'm moving my project package from Spring xml file configuration to class annotation configuration.
Im stuck with a bean instantiation failed on a bean defined in a another context xml file.
This is the definition :
<bean id="mglsChecker" class="DefaultMglsAdapter" destroy-method="close">
<constructor-arg value="${mgls.server.address}"/>
<constructor-arg value="${mgls.fname}"/>
<constructor-arg value="${mgls.lcount}"/>
</bean>
the mglsChecker class is defined in an infrastucture package common to the entire "solution".
The issue is that the variables "${}" are not defined so now this class is not instantiated.
I dont understand how it works when my project is xml file configured.
In the original applicationContext.xml I dont see any references to this mglsChecker context file.
Any help where should i look into ? what am i missing ?
thanks,
You can use
#Configuration
class YourConfig {
// you usually don't need to explicitly give the bean name
// if you don't, Spring gives it the config's method name
#Bean(name = "mglsChecker", destroyMethod = "close")
MglsAdapter mglsChecker(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
return new DefaultMglsAdapter(address, fname, lcount);
}
}
Personally, I prefer creating #Component classes, but for that you need to be able to edit the DefaultMglsAdapter class.
#Component
class DefaultMglsAdapter implements MglsAdapter {
// fields to hold the configs
DefaultMglsAdapter(#Value("${mgls.server.address}") String address,
#Value("${mgls.fname}") String fname,
#Value("${mgls.lcount}") long lcount) {
// set fields
}
#PreDestroy
void close() {
// cleanup
}
}
EDIT: incorporated Andreas' correction :)
Load the properties in the java file via
#Configuration
#PropertySource("classpath:foo.properties")
public class DefaultMglsAdapter{
//...
}
Inject the properties via
#Value( "${mgls.server.address}" )
private String serverAddress;
The variables which are mentioned with "${}" syntax are key/place-holders of properties.
Please search or find such key from *.properties or *.config or *.xml or any such custom properties files. If you find any such properties file then specify classpath or location of that file where you want to configure it as given below:
By XML:
<context:property-placeholder location="classpath:path/to/PropertiesFile"/>
By Annotation:
#Configuration
#PropertySource("classpath:path/to/PropertiesFile")
#Value("${Property}")
Thanks and Regards.

Property does not get filled within dependency jar (Spring 3 / XML)

We have a Spring managed application that uses another jar as a dependency, which contains a Spring managed service class, that need to use some value injected from property file
The main application with the Spring context setup
public static void main(String[] args) {
GenericXmlApplicationContext appContext = new GenericXmlApplicationContext("applicationContext.xml");
SomeClass someClass = (SomeClass) appContext.getBean("someClass");
someClass.someMethod();
...
The class which calls the service from the dependent jar
public class SomeClass {
private ServiceFromTheOtherJar serviceFromTheOtherJar;
public SomeClass(ServiceFromTheOtherJar serviceFromTheOtherJar) {
this.serviceFromTheOtherJar = serviceFromTheOtherJar;
}
public void someMethod() {
serviceFromTheOtherJar.call();
...
The applicationContext.xml of the main app
<bean name="serviceFromTheOtherJar" class="com...ServiceFromTheOtherJar"/>
<bean name="someClass" class="com...SomeClass">
<constructor-arg ref="serviceFromTheOtherJar"/>
</bean>
The service class in the dependent jar
public class ServiceFromTheOtherJar {
private String someFieldWeWantToFillFromPropertyFile;
public void setSomeFieldWeWantToFillFromPropertyFile(String someFieldWeWantToFillFromPropertyFile) {
this.someFieldWeWantToFillFromPropertyFile = someFieldWeWantToFillFromPropertyFile;
}
public void call() {
//we would like to use the filled someFieldWeWantToFillFromPropertyFile here
...
And of course we have an application.properties file in the dependent jar, that contains the property value that we would like to inject into someFieldWeWantToFillFromPropertyFile
Now we can add the dependent jar as a dependency to the main app; when the main app is being executed then its Spring context is getting set up all right, and ServiceFromTheOtherJar.call() method gets called as expected; however someFieldWeWantToFillFromPropertyFile does not get filled from the property file whatever we tried so far (e.g. #PropertySource({"application.properties"}), Environment.getProperty(...) etc.)
Restrictions
We have Spring 3 version in both jars, and that has to remain so due to the deployment environment; so Spring 4 solutions are out of question
As you see above, the main app currently uses GenericXmlApplicationContext and changing that seem to indicate a significant rewriting of the application. Therefore e.g. it seemed to be not possible to use #Service annotation on ServiceFromTheOtherJar because it caused BeanCreationException during the execution and context setup
To read values from property file you have to add the following bean to applicationContext.xml.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:application.properties" />
</bean>
Assuming the application.properties-file contains a definition like this
myValue=Hello World
the definition of the service-bean should by extended like this
<bean name="serviceFromTheOtherJar" class="com...ServiceFromTheOtherJar">
<property name="someFieldWeWantToFillFromPropertyFile" value="${myValue}" />
</bean>
Now Spring will look for the application.properties-file in classpath and set the attribute of the service bean according to myValue.

SpringFramework: set bean name programmatically

For springframwork based application, when using xml to declare beans, bean id can be configured by passing a unique value and even a parameter and then solve the value at runtime.
Now I hope to replace all xml configuration to java annotation.
Say I want to create two database beans with different id.
bean.xml
<bean id="A.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="B.database" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
Then I optimize the upper code to one bean with two property file for two application
bean.xml
<bean id="${appName.database}" class="org.apache.commons.dbcp.BasicDataSource">
<!-- collaborators and configuration for this bean go here -->
</bean>
applicationA.properties
appName.database=A.database
applicationB.properties
appName.database=B.database
The whole application is composed of "framework" module which provides beans common for each application, like database bean, jdbcTemplate bean, and "application" module which provides property value for placeholder and initializes beans with unique id. So even if I start multiple application at the same time, they will find corresponding bean from the context.
Generally speaking, I hope to do
#Bean(name = "${beanName}")
public ABean getBean() {}
and resolve ${beanName} at application level.
By reading SpringFramwork document, I found the answer: BeanNameGenerator
NameGenerator.class
public class NameGenerator implements BeanNameGenerator{
#Override
public String generateBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
if(definition.getBeanClassName().contains("Toto")) {
return "toto";
}
return return definition.getBeanClassName();
}
}
AppConfiguration.class
#Configuration
#ComponentScan(basePackages = {"com.example.domain"}, nameGenerator = NameGenerator.class)
public class Config {
}
Domain class with #Component
#Component
public class Toto {
private int id;
}
BootApplication with domain bean name : toto
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(
DemoApplication.class, args);
for (String name : ctx.getBeanNamesForType(Toto.class)) {
System.out.println(name);
}
}
}
If you want to follow that type of approach create multiple configuration classes you annotated with different Spring profiles.
At start up you can pass a parameter on which profile to use and hence what beans to load within the associated profile.
A more efficient way to do it is use the same property naming convention across all application .properties files. Set a parameter placeholder for the file name which resolves to a JVM arg passed at runtime which is loaded by the#PropertySource annotation.
There's no need to have duplicate beans defined for different environments if it's just properties that are changing.

Injecting property files overriding the one defined in the application

I need some help on injecting property value to a bean which is defined outside the web application.
The web application has a property file under src/main/resource.The spring application context xml has the property place holder defined as
<context:property-placeholder
location="classpath:test.properties,file:/etc/test1.properties"
ignore-resource-not-found="true"
/>
where test1.properties is another file which resides outside the application.The bean is injected with the property which is defined in the application (test.properties) ,but I want to inject the property that is defined in test1.properties (ideally the idea is to override the property values from application and read the one defined outside the application).
Thanks.
Hi use like below in applicationContext.xml
<util:properties id="property" location="classpath:test.properties"/>
In Java,
#Autowired
protected Properties property;
I guess this is what you are looking for
<context:property-placeholder location="file:c:/kp/sec.properties" order="1" ignore-resource-not-found="true" ignore-unresolvable="true" />
<context:property-placeholder location="classpath:kp-props.properties" order="2" />
If the file sec.properties exists take the value from sec.properties, if file or properties does not exist take the property from kp-props.properties file from resources directory(if the property is not found in either of place application will fail)
And say you have property my.prop and you can inject the property as follows.
#Component
public class KPProps {
#Value("${my.prop}")
private int props;
public void print(){
System.out.println(props);
}
}

Spring MVC form validation messages not loading

I'm trying to define error messages in a resource file, but I can't seem to get them to load so I can use them in my controller.
My servlet-context.xml file:
<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="classpath:messages" />
</beans:bean>
My form:
public class LoginForm {
#NotEmpty
private String username;
// getters and setters
}
My messages.properties file (in the root of my source folder, so it gets compiled to the root of my classes folder):
NotEmpty.loginForm.username=test1
NotEmpty.username=test2
NotEmpty.java.lang.String=test3
NotEmpty=test4
My controller:
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#Valid LoginForm form, BindingResult result) {
if (result.getFieldErrors().size() > 0) {
FieldError firstError = result.getFieldErrors().get(0);
System.out.println(firstError.getDefaultMessage());
}
// ...
}
However, the output of the getDefaultMessage call is never what I define in my resource file! It is always may not be empty (the default).
I've tried a variety of different entries in my context file but it seems like it's not loading the resource file. What am I doing wrong?
According to this documentation, all you need is to put a properties file called ValidationMessages.properties at the root of your project classpath.
You can then add properties with the following format
org.hibernate.validator.constraints.NotEmpty.message=Default message, can not be empty
NotEmpty.ClassName.fieldName=fieldName can not be empty.
where ClassName is the name of your class and fieldName is the name of the field of that class.
You might need to do some configuration to set the correct MessageInterpolator, but I think the default one will do what you need.
First idea:
Try to rename property keys from NotEmpty.loginForm.username to NotEmpty (Just to check if messageSource works correctly). If still not working there is
Second idea:
Where do you perform scanning of components?
Assume that you have two spring configurations: applicationContext.xml (or other name) and servlet-context.xml.
If you have such structure <context:component-scan base-package="by.company.app" /> in applicationContext than your controllers have no defined messageSource - so it cannot load customized messages. It is because #Controller bean is in one spring context, MessageSource bean - in another. (To check this you can simple declare #Autowired MessageSource messageSource field in controller and see in debug is it null or not) In that case you can modify component-scan in applicationContext to:
<context:component-scan base-package="by.company.app">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
and add following configuration to servlet-context.xml:
<context:component-scan base-package="by" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
Hope this helps
Unfortunately it looks like a MessageSource must be defined and then used in the appropriate Java class. The getDefaultMessage() method doesn't appear able to read from message properties.
In a component-scanned class:
#Autowired
private MessageSource messageSource;
public String getMessage(FieldError field) {
return messageSource.getMessage(field, null);
}
This will iterate through all possibilities in the message properties files and then--if nothing is found--falls back on the getDefaultMessage() results.
Additionally I updated my servlet-context.xml file to define my messageSource with the basename value of /WEB-INF/classes/messages, as opposed to what I had in my question above.
I spent some time to fix this issue, I done a following changes, now it is working fine in Spring 4.0 MVC and Hibernate 4.1.9
1.put messages under source folder.
If ReloadableResourceBundleMessageSource is not working change to
class="org.springframework.context.support.ResourceBundleMessageSource">
changes property like '<'property name="basename" value="messages"/>
In Message.properties file, the key should be in the following format.
NotEmpty.userForm.userId
userForm-->Model class name.
(If your Model class name is UserForm, then it should
be mentioned as userForm)
userId--> attribute of userForm class

Categories