SpringBatch : dynamic datasource values - java

I have found this subject that answered to what I was looking for :
how to pass values dynamically in config file
The thing is, when I try it, I have an Exception..
Error creating bean with name 'jobOperator' defined in class path resource [atlentic-Spring-Batch-common.xml]: Cannot resolve reference to bean 'jobExplorer' while setting bean property 'jobExplorer' [...]
Error creating bean with name 'connex' defined in class path resource [batch-calendar-context.xml]: Error setting property values;[...] Bean property 'dataSource' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
I'm trying to read a .ini file where I get DB info, then I would like to inject them into my XML datasource config.
Here is my xml,
<beans:bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<beans:property name="driverClassName" value="${DB_DRIVER}" />
<beans:property name="url"
value="${DB_PROTOCOL}:#${DB_HOST}:${DB_PORT}:${DB_NAME}" />
<beans:property name="username" value="#{connex.user}" />
<beans:property name="password" value="#{connex.pass}" />
</beans:bean>
<beans:bean id="connex" class="com.sponge.bob.calendar.entity.CustomConnexion">
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
Then my CustomConnexiob.class where I use constructor to instantiate my attributs (it is not sexy, but I'm starting with SpringBatch) :
#Component
#Scope("step")
public class CustomConnexion {
public String user;
public String pass;
public String base;
#Autowired
private static final Logger LOGGER = LoggerFactory.getLogger(CustomConnexion.class);
public CustomConnexion() {
initConnexion();
}
public void initConnexion() {
IniReader reader = new IniReader();
setUser(reader.getProperty(Constants.MYCOMMON, Constants.USER));
setBase(reader.getProperty(Constants.MYCOMMON, Constants.BASE));
setPass(reader.getProperty(Constants.MYCOMMON, Constants.PASS));
}
/* getters and setters after this line (not printed here but they have the default name */
}
Is it possible to get this password and user dynamically using this way, I begin to lose my mind ?

Deinum,
thank you for your answer ! I tried to use UserCrendentialsDataSourceAdapter, but I didn't manage to make it work. But your observation about the scope make me try something I tried before writing this post.
Finally I used this :
<beans:bean id="connex" class="com.sponge.bob.calendar.entity.CustomConnexion">
</beans:bean>
<beans:bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<beans:property name="driverClassName" value="${DB_DRIVER}" />
<beans:property name="url" value="${DB_PROTOCOL}:#${DB_HOST}:${DB_PORT}:${DB_NAME}" />
<beans:property name="username" value="#{connex.user}"/>
<beans:property name="password" value="#{connex.pass}"/>
</beans:bean>
and
#Component
#Scope("singleton") // <-- I changed this (it was "step" before)
public class CustomConnexion {
public String user;
public String pass;
public String base;
#Autowired
private static final Logger LOGGER = LoggerFactory.getLogger(CustomConnexion.class);
public CustomConnexion() {
initConnexion();
}
public void initConnexion() {
IniReader reader = new IniReader();
setUser(reader.getProperty(Constants.MYCOMMON, Constants.USER));
setBase(reader.getProperty(Constants.MYCOMMON, Constants.BASE));
setPass(reader.getProperty(Constants.MYCOMMON, Constants.PASS));
}
/* getters and setters after this line (not printed here but they have the default name */
}
my IniReader() just parse the .ini

I think you are getting username and password as null.
Remove calling initConnexion() from its constructor.
Add below annotation on top of initConnexion()
#PostConstruct

Related

How to inject a dependency of a constructor with multiple variables to spring xml?

I have a test class created. I need to run the test file using spring dependency injection . I am using the XML method.
How to define a bean for Inventory in your XML configuration?
Testfile constructor:
public VendingMachineServiceLayerImplTest() {
Inventory testItem = new Inventory(2);
testItem.setItemName("Item2");
testItem.setCost(new BigDecimal("4.50"));
testItem.setNoOfItems(0);
VendingMachineDao dao = new VendingMachineDaoStubImpl(testItem);
VendingMachineAuditDao auditDao = new VendingMachineAuditDaoStubImpl();
VendingMachineChange change = new VendingMachineChange();
service = new VendingMachineServiceLayerImpl(dao, auditDao, change);
}
Bean Definition in applicationContext.xml
<bean id="dao"
class="mthree.vendingmachine.dao.VendingMachineDaoFileImpl"/>
<bean id="daoStub"
class="vendingmachine.dao.VendingMachineDaoStubImpl"/>
<bean id="change"
class="vendingmachine.dao.VendingMachineAuditDaoStubImpl"/>
<bean id="change" class="vendingmachine.dao.VendingMachineChange"/>
<bean id="serviceLayer"
class="vendingmachine.service.VendingMachineServiceLayerImpl">
<constructor-arg ref="dao"/>
<constructor-arg ref="auditDaoStub"/>
<constructor-arg ref="change"/>
</bean>
Inventory Class
public class Inventory {
private int itemNumber;
private String itemName;
private BigDecimal cost;
private int noOfItems;
public Inventory(int itemNumber){
this.itemNumber=itemNumber;
}
public int getItemNumber() {
return itemNumber;
}
//other getters and setters
}
How can i inject the constructor with the object? I have tried adding properties but the test error occurred as the properties couldn't parse.
Thanks in advance
Need to include the value that is in the constructor in the <constructor-arg> tag and other properties in the <property> tag in the Inventory Class as follows.
<bean id="daoStub"
class="mthree.vendingmachine.dao.VendingMachineDaoStubImpl"/>
<bean id="auditDaoStub"
class="mthree.vendingmachine.dao.VendingMachineAuditDaoStubImpl"/>
<bean id="change" class="mthree.vendingmachine.service.VendingMachineChange"/>
<bean id="inventory" class="vendingmachine.dto.Inventory">
<constructor-arg name = "itemNumber" value = "2"/>
<property name = "itemName" value = "Item2"/>
<property name = "cost" value = "5"/>
<property name = "noOfItems" value = "0"/>
</bean>
<bean id="dao" class="vendingmachine.dao.VendingMachineDaoStubImpl">
<constructor-arg ref = "inventory"/>
</bean>
<bean id="serviceLayer"
class="vendingmachine.service.VendingMachineServiceLayerImpl">
<constructor-arg ref="dao"/>
<constructor-arg ref="auditDaoStub"/>
<constructor-arg ref="change"/>
</bean>

How to define a bean in ApplicationContext.xml that has no constructor?

I have a class
public class DataStore {
public String name;
public String username;
public String password;
public String token;
public String connectionString;
public String type;
public String scheme;
public boolean usesBasicAuth;
public boolean usesBearerAuth;
}
I need to create an bean for it in another project. But i need to fill the fields somehow. The problem is I can not use <constructor-arg ... /> because there is no constructor.
The code below results in BeanCreationException: "Could not resolve matching constructor"
<bean id="dataStore"
class="com.fressnapf.sdk.dataaccess.services.DataStore">
<constructor-arg index="0" value="${spring.datastore.name}"/>
...
</bean>
Assuming, You have public (getters and setters for your) properties, and only the "default (no args) constructor", then You can change your configuration to:
<bean id="dataStore" class="com.fressnapf.sdk.dataaccess.services.DataStore">
<property name="connectionString" value="..."/>
<!-- ... -->
</bean>
Using property instead of constructor-arg.
Docs (Spring 4.2): https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/xsd-configuration.html
Yes, there is a constructor.
If you don't explicitly put one, Java will automatically add a default (non argument) constructor.
Use that one.
It will set all the instance variables to the default value of their type.
In your case: null for every String variable, false for every boolean.

NotWritablePropertyException - When context is initialized - Spring

This is quite a common question in spring community. However even after referring many suggestions and answers, I couldn't get the below program to work correctly.
I need to inject a property of String type in Spring bean.
Here is the bean definition:
<bean id="AzoneMessageTransformer" class="com.test.wsg.RequestMessageTransformer">
<property name="converAndMarshal" value="false" />
</bean>
<bean id="BzoneMessageTransformer" class="com.test.wsg.RequestMessageTransformer">
<property name="converAndMarshal" value="false" />
<property name="authCode" >
<value>BZ</value>
</property>
</bean>
Here is the Spring bean:
public class RequestMessageTransformer implements InitializingBean {
private String authCode = null;
private boolean converAndMarshal = true;
public void setConverAndMarshal(boolean converAndMarshal) {
this.converAndMarshal = converAndMarshal;
}
public boolean isConverAndMarshal() {
return converAndMarshal;
}
public void setAuthCode(String authCode) {
this.authCode = authCode;
}
public String getAuthCode() {
return authCode;
}
}
Receiving the below error when context is getting initialized:
at java.lang.Thread.run(Thread.java:744)
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'authCode' of bean class [com.test.wsg.RequestMessageTransformer]: Bean property 'authCode' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1067)
at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:926)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:75)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1502)
Any help and suggestions for pointing out my mistake is much appreciated.
Thanks in advance!
authCode is string property, you should set property something like this
<bean id="BzoneMessageTransformer" class="com.test.wsg.RequestMessageTransformer">
<property name="converAndMarshal" value="false" />
<property name="authCode" value="BZ" />
</bean>

Scope of bean and reference bean

In a spring application,
Say Bean person is of scope prototype
and Bean address is of scope singleton
Class person
{
string name;
int age;
int rollno;
Address address;
// Getters & Setters
}
Class Address
{
int door_no;
string street_name;
// Getters & Setters
}
It is obvious that a new instance will be created for Person each time when requested.
now will a new instance be created for every request for Address or since it is referenced from person it will have the scope - prototype?
In sringBean.xml
<bean id="studentBeanId" class="com.saro.corespring.autowire.Person"
autowire="byName" scope="prototype">
<property name="name" value="saro" />
<property name="age" value="25" />
<property name="roll_no" value="101" />
</bean>
<bean id="addressBean" class="com.saro.corespring.autowire.Address" autowire="byType" scope="singleton">
<property name="door_no" value="10" />
<property name="street_name" value="Street Name" />
</bean>

Reading 2 property files having same variable names in Spring

I am reading property files using below entry in my Spring xml.
<context:property-placeholder
location="classpath:resources/database1.properties,
classpath:resources/licence.properties"/>
I am injecting this values in variable using xml entry or using #Value annotation.
<bean id="myClass" class="MyClass">
<property name="driverClassName" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="name" value="${database.name}" />
</bean>
I want to add a new property file(database2.properties) which has few same variable names as of database1.properties.
database1.properties:
database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://192.168.1.10/
database.name=dbname
database2.properties:
database.url=jdbc:mysql://192.168.1.50/
database.name=anotherdbname
database.user=sampleuser
You can see few property variables have same name like database.url, database.name in both the property files.
Is it possible to inject database.url of database2.properties?
Or I have to change variable names?
Thank you.
You can do it by configuring two PropertyPlaceholderConfigurer. Usually there's only one instance that serves out all the properties, however, if you change the placeholderPrefix you can use two instances, something like
<bean id="firstPropertyGroup" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:resources/database1.properties,
classpath:resources/licence.properties" />
<property name="placeholderPrefix" value="${db1."/>
</bean>
<bean id="secondPropertyGroup" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:resources/database2.properties" />
<property name="placeholderPrefix" value="${db2."/>"
</bean>
Then you would access your properties like ${db1.database.url} or ${db2.database.url}
There might be a solution, similar to what's that you want to achieve. Check the second answer to this question: Multiple properties access. It basically explains what to do in order to access the properties of the second file by using another expression, which is defined by you.
Otherwise, the simplest solution would be just changing the key values (the variable names).
You will sooner or later switch to Spring Boot. So with Spring Boot you can do have such POJO:
public class Database {
#NotBlank
private String driver;
#NotBlank
private String url;
#NotBlank
private String dbname;
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDbname() {
return dbname;
}
public void setDbname(String dbname) {
this.dbname = dbname;
}
}
and use #ConfigurationProperties to fill it:
#Bean
#ConfigurationProperties(locations="classpath:database1.properties", prefix="driver")
public Database database1(){
return new Database();
}
#Bean
#ConfigurationProperties(locations="classpath:database2.properties", prefix="driver")
public Database database2(){
return new Database();
}
Downside of this is that it's mutable. With Lombok library, you can eliminate nasty getters and setters.

Categories

Resources